home *** CD-ROM | disk | FTP | other *** search
/ World of Video / World of Video.iso / gfxprograms / 3dprograms / t3dlib / source / read.c < prev    next >
C/C++ Source or Header  |  1995-02-13  |  77KB  |  2,261 lines

  1. /* read.c - read a TTDDD file and fill the structures
  2.  *        - written by Glenn M. Lewis - 7/19/91
  3.  */
  4.  
  5. static char rcs_id[] = "$Id: read.c,v 1.32 1993/12/11 22:22:19 glewis Exp $";
  6.  
  7. #include <stdio.h>
  8. #include <ctype.h>
  9. #include <sys/types.h>
  10. #include "t3dlib.h"
  11. #ifdef __STDC__
  12. #include <stdlib.h>
  13. #include <strings.h>
  14. #include "read_protos.h"
  15. #endif
  16.  
  17. static void process_INFO();
  18. static void process_OBJ();
  19. static void process_ISTG();
  20. static OBJECT *process_EXTR();
  21. static void process_DESC();
  22. static OBJECT *process_MXTR();
  23. static void desc_copy();
  24. static void malloc_arrays();
  25. extern int verbose_flag;
  26. extern int already_read_header;            /* A hack to include characters */
  27. extern unsigned char header_storage[];    /* that were already read in */
  28. extern void insert_into_sorted_list();
  29.  
  30. #define MAXLINE 512
  31. #define get_UBYTE    (UBYTE)get_num
  32. #define get_UWORD    (UWORD)get_num
  33. #define get_ULONG    (ULONG)get_num
  34.  
  35. static char strin[MAXLINE+1], ps[MAXLINE+1];
  36. static UBYTE defclst[3], defrlst[3], deftlst[3], defspc1[3];
  37.  
  38. /* Here are a few necessary utilities */
  39.  
  40. static void warn(curline)
  41. int curline;
  42. {
  43.     fprintf(stderr, "WARNING: Line %d: ", curline);
  44. }
  45.  
  46. static void parse_word(name, lim, up)    /* Get next word, optionally limit # of chars */
  47. register char *name;
  48. int lim, up;
  49. {
  50.     register char *c = &strin[0];
  51.     register int i;
  52.     int quote = 0;
  53.  
  54.     while (*c==' ') c++;        /* Skip over leading spaces */
  55.     for (i=0; *c; i++,c++) {
  56.         if (lim && i>=lim) break;    /* No more characters desired */
  57.         if (*c == '\"') {            /* Chop off quotes, ignore spaces */
  58.             if (quote) break;        /* Second quote.  Stop reading string */
  59.             quote=1;
  60.             i--;                    /* Don't want i to increment */
  61.             continue;
  62.         }
  63.         if (up && !quote && islower(*c)) *name++ = toupper(*c);
  64.         else {
  65.             if (!quote && *c==' ') break;    /* End of the line */
  66.             *name++ = *c;            /* Don't modify original case */
  67.         }
  68.     }
  69.     *name = '\0';    /* Truncate the puppy */
  70.     /* Now, delete the command from 'strin' */
  71.     while (*c && *c!=' ') c++;    /* Skip the end of the command */
  72.     while (*c==' ') c++;        /* Skip mid-word spacing */
  73.     strcpy(strin, c);            /* Chop off front of line */
  74. }
  75.  
  76. static int get_line(strin, w)
  77. register char *strin;
  78. WORLD *w;
  79. {
  80.     static int comment = 0;
  81.     int quote = 0, start;
  82.     register char *c;
  83.  
  84. GET_MORE:
  85.     if (comment) start = 0; else start = -1;
  86.     if (fgets(&strin[already_read_header], MAXLINE, w->inp) == NULL) {
  87.         already_read_header = 0;
  88.         if (comment)
  89.         fprintf(stderr, "WARNING: Line %d: Unterminated comment.\n",w->cur_line);
  90.         return(0);    /* EOF */
  91.     }
  92.     already_read_header = 0;
  93.     w->cur_line++;
  94.     for (c=strin; *c; c++) {
  95.         if (*c=='/' && c[1]=='*') {            /* Begin of comment */
  96.             if (!quote) {
  97.                 if (!comment++) {            /* First comment */
  98.                     start = (int)(c-strin);
  99.                 }
  100.             }
  101.         } else
  102.         if (*c=='*' && c[1]=='/') {            /* End of comment */
  103.             if (!quote) {
  104.                 comment--;
  105.                 if (comment<0) {
  106.                     fprintf(stderr, "ERROR!  '*/' before '/*'\n*** ABORT ***\n");
  107.                     exit(20);
  108.                 }
  109.                 if (!comment) {                /* Chop off front of line */
  110.                     strcpy(strin, c+2);
  111.                     c = strin-1;            /* Pointer will increment */
  112.                 }
  113.             }
  114.         } else
  115.         if (*c=='%') {                        /* Line comment */
  116.             if (!(quote || comment)) {
  117.                 *c = '\0';                    /* Terminate line */
  118.                 break;
  119.             }
  120.         } else
  121.         if (*c =='\"') {                    /* Quote */
  122.             if (!comment) quote=1-quote;    /* Toggle flag */
  123.         } else
  124.         if (*c == '\t') *c = ' ';            /* Change tabs to spaces */
  125.         else
  126.         if (!quote && (*c=='[' || *c==']' || *c=='=')) *c = ' ';
  127.         if (*c == '\n') { *c = '\0'; break; }
  128.     }
  129.     if (comment) strin[start] = '\0';        /* Chop off comment */
  130. /* Skip leading white space to see if this line contains anything */
  131.     for (c=strin; *c==' '; c++) ;
  132.     if (!*c) goto GET_MORE;
  133.     return(1);
  134. }
  135.  
  136. static int valid_num;
  137.  
  138. static long get_num()
  139. {
  140.     long val = 0;
  141.     char neg = ' ';
  142.     register char *c = &strin[0];
  143.  
  144.     valid_num = 0;
  145.     while (*c == ' ') c++;        /* Skip leading space */
  146.     if (*c == '-') neg = *c++;    /* Save the negation  */
  147.     if (!isdigit(*c)) return(0L);
  148.     while (*c && isdigit(*c))
  149.         val = (val*10) + (*c++ - '0');
  150.     while (*c == ' ') c++;        /* Skip trailing space */
  151.     strcpy(strin, c);            /* Chop off number */
  152.     valid_num = 1;
  153.     return((neg=='-' ? -val : val));
  154. }
  155.  
  156. static int valid_FRACT;
  157.  
  158. static double get_FRACT()
  159. {
  160.     register long whole, expo;
  161.     register char *s = &strin[0];
  162.     char neg=' ', negexp=' ';
  163.     double f, frac, place;
  164.  
  165.     valid_FRACT = 0;
  166.     whole = expo = 0;
  167.     while (*s == ' ') s++;
  168.     if (*s == '-') neg = *s++;
  169.     if (!isdigit(*s) && *s!='.') return(0L);    /* Invalid FRACT */
  170.     while (*s && isdigit(*s))        /* Handle float before decimal point */
  171.         whole = (whole * 10) + (*s++ - '0');
  172.     if (*s == '.') {        /* Handle float after decimal point */
  173.         s++;
  174.         frac = 0.0;
  175.         for (place=10.0; *s && isdigit(*s); place*=10.0)
  176.             frac +=  (*s++ - '0')/place;
  177.         f = (double)whole + frac;
  178.     } else f = (double)whole;
  179.     if (*s == 'e' || *s == 'E') {    /* Support exponential notation */
  180.         s++;
  181.         if (*s=='-' || *s=='+') negexp = *s++;
  182.         while (*s && isdigit(*s)) expo = (expo*10) + (*s++ - '0');
  183.         if (negexp=='-')    while (expo--) f/=10.0;
  184.         else                while (expo--) f*=10.0;
  185.     }
  186.     while (*s == ' ') s++;
  187.     strcpy(strin, s);
  188.     valid_FRACT = 1;
  189.     return((neg=='-' ? -f : f));
  190. }
  191.  
  192. static void stage_RGB(st)
  193. XYZ_st *st;
  194. {
  195.     register char *c = &strin[0];
  196.     register int i, flag, cnt;
  197.  
  198.     flag = 0;    /* If they use 'R','G', or 'B', they must continue to use it */
  199.     /* On the other hand, if they give only a single parameter, then it applies to all */
  200.     cnt = 0;
  201.     for (i=0; i<3; i++) {
  202.         if (*c=='r' || *c=='R')        /* If !valid, it's an ERROR! */
  203.         { *c=' '; st->x = get_FRACT(); if (valid_FRACT) flag=1; }
  204.         else
  205.         if (*c=='g' || *c=='G')
  206.         { *c=' '; st->y = get_FRACT(); if (valid_FRACT) flag=1; }
  207.         else
  208.         if (*c=='b' || *c=='B')
  209.         { *c=' '; st->z = get_FRACT(); if (valid_FRACT) flag=1; }
  210.         else if (!flag) {
  211.             if (i==0)        st->x = get_FRACT();
  212.             else if (i==1)    st->y = get_FRACT();
  213.             else            st->z = get_FRACT();
  214.             if (valid_FRACT) cnt++;
  215.         }
  216.     }
  217.     if (!flag && cnt==1) {     /* Copy value into other two elements */
  218.         st->y = st->z = st->x;
  219.     }
  220. }
  221.  
  222. static void stuff_XYZ(st)
  223. XYZ_st *st;
  224. {
  225.     register char *c = &strin[0];
  226.     register int i, flag, cnt;
  227.  
  228.     flag = 0;    /* If they use 'X','Y', or 'Z', they must continue to use it */
  229.     /* On the other hand, if they give only a single parameter, then it applies to all */
  230.     cnt = 0;
  231.     for (i=0; i<3; i++) {
  232.         if (*c=='x' || *c=='X')        /* If !valid, it's an ERROR! */
  233.         { *c=' '; st->x = get_FRACT(); if (valid_FRACT) flag=1; }
  234.         else
  235.         if (*c=='y' || *c=='Y')
  236.         { *c=' '; st->y = get_FRACT(); if (valid_FRACT) flag=1; }
  237.         else
  238.         if (*c=='z' || *c=='Z')
  239.         { *c=' '; st->z = get_FRACT(); if (valid_FRACT) flag=1; }
  240.         else if (!flag) {
  241.             if (i==0)        st->x = get_FRACT();
  242.             else if (i==1)    st->y = get_FRACT();
  243.             else            st->z = get_FRACT();
  244.             if (valid_FRACT) cnt++;
  245.         }
  246.     }
  247.     if (!flag && cnt==1) {     /* Copy value into other two elements */
  248.         st->y = st->z = st->x;
  249.     }
  250. }
  251.  
  252. static void stuff_RGB(st)
  253. RGB_st *st;
  254. {
  255.     register char *c = &strin[0];
  256.     register int i, flag, cnt;
  257.  
  258.     flag = 0;    /* If they use 'R','G', or 'B', they must continue to use it */
  259.     /* On the other hand, if they give only a single parameter, then it applies to all */
  260.     cnt = 0;
  261.     for (i=0; i<3; i++) {
  262.         if (*c=='r' || *c=='R')        /* If !valid, it's an ERROR! */
  263.         { *c=' '; st->r = get_UBYTE(); if (valid_num) flag=1; }
  264.         else
  265.         if (*c=='g' || *c=='G')
  266.         { *c=' '; st->g = get_UBYTE(); if (valid_num) flag=1; }
  267.         else
  268.         if (*c=='b' || *c=='B')
  269.         { *c=' '; st->b = get_UBYTE(); if (valid_num) flag=1; }
  270.         else if (!flag) {
  271.             if (i==0)        st->r = get_UBYTE();
  272.             else if (i==1)    st->g = get_UBYTE();
  273.             else            st->b = get_UBYTE();
  274.             if (valid_num) cnt++;
  275.         }
  276.     }
  277.     if (!flag && cnt==1) {     /* Copy value into other two elements */
  278.         st->g = st->b = st->r;
  279.     }
  280. }
  281.  
  282. /********************/
  283. /* The MAIN section */
  284. /********************/
  285.  
  286. WORLD *read_TTDDD(file)
  287. FILE *file;
  288. {
  289.     char name[5];
  290.     WORLD *world;
  291.  
  292.     if (!file) return(0L); /* File not open */
  293.  
  294.     if (!(world = (WORLD*)malloc(sizeof(WORLD)))) { OUT_MEM("WORLD"); }
  295.     bzero((char*)world, sizeof(WORLD));
  296.     world->inp = file;
  297.  
  298.     if (already_read_header)    /* Copy the data into the "strin" */
  299.         strncpy(strin, header_storage, already_read_header);
  300.  
  301. /* Here is the main loop: */
  302.     while (get_line(strin, world)) {
  303.         parse_word(name, 4, 1);        /* And chop it out of the string */
  304.  
  305.         if        (strcmp(name, "INFO")==0) process_INFO(world);
  306.         else if (strcmp(name, "OBJ") ==0) process_OBJ(world);
  307.         else if (strcmp(name, "ISTG")==0) process_ISTG(world);
  308.         else {
  309.             fprintf(stderr, "Invalid chunk on line %d: '%s' (skipped)\n",
  310.                 world->cur_line, name);
  311.             continue;
  312.         }
  313.     }
  314.  
  315. /* All done.  Close up shop. */
  316.     return(world);
  317. }
  318.  
  319. static void process_INFO(world)
  320. WORLD *world;
  321. {
  322.     register INFO *info;
  323.     register ULONG i;
  324.     char this_level[MAXLINE], name[5];
  325.  
  326.     if (world->info) {
  327.         fprintf(stderr, "Parse error: More than one INFO chunk!\n"); exit(-1); }
  328.     if (!(info = world->info = (INFO*)malloc(sizeof(INFO)))) { OUT_MEM("INFO"); }
  329.     bzero((char *)world->info, sizeof(INFO));
  330.  
  331.     parse_word(this_level, 0, 1);    /* No limit, uppercase. */
  332.     if (strcmp(this_level, "BEGIN") != 0) {
  333.         fprintf(stderr, "WARNING: Line %d: Bad syntax.  Missing 'Begin' after 'INFO'\n",
  334.             world->cur_line);
  335.     }
  336.     parse_word(this_level, 0, 0);    /* No limit, original case */
  337.  
  338.     while (get_line(strin, world)) {
  339.         parse_word(name, 4, 1);        /* Get command */
  340.         if (strcmp(name, "BRSH")==0) {
  341.             i = get_UWORD();
  342.             if (i<0 || i>7) fputs("BRSH error.\n", stderr);
  343.             parse_word(ps, 0, 0);
  344.             strncpy(info->brsh[i], ps, 80);
  345.             info->brsh[i][80]='\0';
  346.         } else
  347.         if (strcmp(name, "STNC")==0) {
  348.             i = get_UWORD();
  349.             if (i<0 || i>7) fputs("STNC error.\n", stderr);
  350.             parse_word(ps, 0, 0);
  351.             strncpy(info->stnc[i], ps, 80);
  352.             info->stnc[i][80]='\0';
  353.         } else
  354.         if (strcmp(name, "TXTR")==0) {
  355.             i = get_UWORD();
  356.             if (i<0 || i>7) fputs("TXTR error.\n", stderr);
  357.             parse_word(ps, 0, 0);
  358.             strncpy(info->txtr[i], ps, 80);
  359.             info->txtr[i][80]='\0';
  360.         } else
  361.         if (strcmp(name, "OBSV")==0) {
  362.             if (!info->obsv) info->obsv=(OBSV*)malloc(sizeof(OBSV));
  363.             if (!info->obsv) { OUT_MEM("OBSV"); }
  364.             parse_word(name, 4, 1);        /* Get field */
  365.             if        (strcmp(name, "CAME")==0) stuff_XYZ(&info->obsv->came);
  366.             else if (strcmp(name, "ROTA")==0) stuff_XYZ(&info->obsv->rota);
  367.             else if (strcmp(name, "FOCA")==0) info->obsv->foca = get_FRACT();
  368.             else {
  369.                 if (verbose_flag)
  370.                     fprintf(stderr, "WARNING: Line %d: Unknown OBSV field: '%s'\n",
  371.                         world->cur_line, name);
  372.                 continue;
  373.             }
  374.         } else
  375.         if (strcmp(name, "OTRK")==0) {
  376.             parse_word(ps, 0, 0);
  377.             strncpy(info->otrk, ps, 18);
  378.             info->otrk[18]='\0';
  379.         } else
  380.         if (strcmp(name, "OSTR")==0) {
  381.             if (!info->ostr) info->ostr=(STRY*)malloc(sizeof(STRY));
  382.             if (!info->ostr) { OUT_MEM("OSTR"); }
  383.             parse_word(name, 4, 1);        /* Get field */
  384.             if (strcmp(name, "PATH")==0) parse_word(info->ostr->path, 18, 0);
  385.             else if (strcmp(name, "TRAN")==0) stuff_XYZ(&info->ostr->tran);
  386.             else if (strcmp(name, "ROTA")==0) stuff_XYZ(&info->ostr->rota);
  387.             else if (strcmp(name, "SCAL")==0) stuff_XYZ(&info->ostr->scal);
  388.             else
  389.             if (strcmp(name, "INFO")==0) {
  390.                 info->ostr->info = 0;
  391.                 while (strin[0]) {
  392.                     parse_word(ps, 7, 1);
  393.                     if        (strcmp(ps, "ABS_TRA")==0) info->ostr->info|=(1<<0);
  394.                     else if (strcmp(ps, "ABS_ROT")==0) info->ostr->info|=(1<<1);
  395.                     else if (strcmp(ps, "ABS_SCL")==0) info->ostr->info|=(1<<2);
  396.                     else if (strcmp(ps, "LOC_TRA")==0) info->ostr->info|=(1<<4);
  397.                     else if (strcmp(ps, "LOC_ROT")==0) info->ostr->info|=(1<<5);
  398.                     else if (strcmp(ps, "LOC_SCL")==0) info->ostr->info|=(1<<6);
  399.                     else if (strcmp(ps, "X_ALIGN")==0) info->ostr->info|=(1<<8);
  400.                     else if (strcmp(ps, "Y_ALIGN")==0) info->ostr->info|=(1<<9);
  401.                     else if (strcmp(ps, "Z_ALIGN")==0) info->ostr->info|=(1<<10);
  402.                     else if (strcmp(ps, "FOLLOW_")==0) info->ostr->info|=(1<<12);
  403.                     else {
  404.                         if (verbose_flag)
  405.                             fprintf(stderr, "WARNING: Line %d: Unknown OSTR INFO flag: '%s'\n",
  406.                                 world->cur_line, ps);
  407.                         continue;
  408.                     }
  409.                 }
  410.             } else {
  411.                 if (verbose_flag)
  412.                     fprintf(stderr, "WARNING: Line %d: Unknown OSTR field: '%s'\n",
  413.                         world->cur_line, name);
  414.                 continue;
  415.             }
  416.         } else
  417.         if (strcmp(name, "FADE")==0) {
  418.             if (!info->fade) info->fade=(FADE*)malloc(sizeof(FADE));
  419.             if (!info->fade) { OUT_MEM("FADE"); }
  420.             parse_word(ps, 6, 1);        /* Get field */
  421.             if        (strcmp(ps, "FADEAT")==0) info->fade->at = get_FRACT();
  422.             else if (strcmp(ps, "FADEBY")==0) info->fade->by = get_FRACT();
  423.             else if (strcmp(ps, "FADETO")==0) stuff_RGB(&info->fade->to);
  424.             else {
  425.                 if (verbose_flag)
  426.                     fprintf(stderr, "WARNING: Line %d: Unknown FADE field: '%s'\n",
  427.                         world->cur_line, ps);
  428.                 continue;
  429.             }
  430.         } else
  431.         if (strcmp(name, "SKYC")==0) {
  432.             if (!info->skyc) info->skyc=(SKYC*)malloc(sizeof(SKYC));
  433.             if (!info->skyc) { OUT_MEM("SKYC"); }
  434.             parse_word(name, 4, 1);        /* Get field */
  435.             if        (strcmp(name, "HORI")==0) stuff_RGB(&info->skyc->hori);
  436.             else if (strcmp(name, "ZENI")==0) stuff_RGB(&info->skyc->zeni);
  437.             else {
  438.                 if (verbose_flag)
  439.                     fprintf(stderr, "WARNING: Line %d: Unknown SKYC field: '%s'\n",
  440.                         world->cur_line, name);
  441.                 continue;
  442.             }
  443.         } else
  444.         if (strcmp(name, "AMBI")==0) {
  445.             if (!info->ambi) info->ambi=(RGB_st*)malloc(sizeof(RGB_st));
  446.             if (!info->ambi) { OUT_MEM("AMBI"); }
  447.             stuff_RGB(info->ambi);
  448.         } else
  449.         if (strcmp(name, "GLB0")==0) {
  450.             if (!info->glb0) info->glb0=(BYTE*)malloc(8*sizeof(BYTE));
  451.             if (!info->glb0) { OUT_MEM("GLB0"); }
  452.             i = get_ULONG();    /* Index into array */
  453.             if (i>=0 && i<8) {
  454.                 info->glb0[i] = (BYTE) get_ULONG();
  455.             } else
  456.                 fprintf(stderr, "WARNING: Line %d: Bad index: GLB0[%d] should be [0..7]\n", world->cur_line, i);
  457.         } else if (strcmp(name, "END")==0) {
  458.             parse_word(name, 4, 1);
  459.             if (strcmp(name, "INFO")!=0)
  460.                 fprintf(stderr, "WARNING: Line %d: Expected 'END INFO' but got: 'END %s'\n", world->cur_line, name);
  461.             parse_word(ps, 0, 0);    /* Get the quote, if any */
  462.             if (strcmp(this_level, ps)!=0)
  463.                 fprintf(stderr, "WARNING: Line %d: 'INFO Begin' and 'End INFO' quoted comments do not match\n", world->cur_line);
  464.             break;
  465.         } else if (verbose_flag)
  466.             fprintf(stderr, "WARNING: Line %d: Unknown INFO sub-sub-chunk: '%s'\n",
  467.                 world->cur_line, name);
  468.     }
  469. }
  470.  
  471. OBJECT *create_object()
  472. {
  473.     OBJECT *p;
  474.     p = (OBJECT*)malloc(sizeof(OBJECT));
  475.     if (!p) { OUT_MEM("Create"); }
  476.     bzero((char*)p,sizeof(OBJECT));
  477.     return(p);
  478. }
  479.  
  480. static void process_OBJ(world)
  481. WORLD *world;
  482. {
  483.     char this_level[MAXLINE], name[5];
  484.     register OBJECT *p;
  485.     int depth;
  486.  
  487.     parse_word(this_level, 0, 1);    /* No limit, uppercase. */
  488.     if (strcmp(this_level, "BEGIN") != 0) {
  489.         fprintf(stderr, "WARNING: Line %d: Bad syntax.  Missing 'Begin' after 'OBJ'\n",
  490.             world->cur_line);
  491.     }
  492.     parse_word(this_level, 0, 0);    /* No limit, original case */
  493.  
  494.     depth=0;
  495.     while (get_line(strin, world)) {
  496.         parse_word(name, 4, 1);        /* Get command */
  497.         if        (strcmp(name, "EXTR")==0) {
  498.             p = process_EXTR(create_object(), world);
  499.             if (world->curobj) {
  500.                 world->curobj->next = p;
  501.                 p->parent = world->curobj->parent;
  502.             } else {
  503.                 world->object = p;
  504.                 p->parent = 0;
  505.             }
  506.             world->curobj = p;
  507.         
  508. /*
  509.  
  510. Glenn:
  511.     Here is the merging code.  I also tried loading grouped objects into
  512. the detail editor, with similar results to what I was seeing when I was loading
  513. massive numbers of separate objects.  Results are below, but I think this will
  514. complicate our lives considerably.
  515.     Cheers,
  516.     Charles
  517.  
  518. ------------------------------------------------------------------------------
  519. Charles Congdon                                      Oracle Corporation
  520. Special Projects                                     2580 SW 192nd Ave.
  521. Midrange Systems Group, DEC Products Division        Aloha, OR   97006
  522. Office:  (415) 506-6341                              FAX:  (415) 506-6341
  523. Internet:  ccongdon@us.oracle.com               Usenet:  uunet!oracle!ccongdon
  524. ------------------------------------------------------------------------------
  525.  
  526.              MODIFICATIONS TO READ.C TO SUPPORT MXTR CHUNKS
  527.          ==============================================
  528.  
  529. Purpose:
  530.  
  531.     These routines allow the following construct:
  532.     
  533.     MXTR Begin
  534.     
  535.     EXTR Begin
  536.     <etc>
  537.     End EXTR
  538.     
  539.     EXTR Begin
  540.     <etc>
  541.     End EXTR
  542.     
  543.     EXTR Begin
  544.     <etc>
  545.     End EXTR
  546.     :
  547.     :
  548.     :
  549.     
  550.     End MXTR
  551.  
  552. Everything within the MXTR chunk is merged into one huge object.  Over
  553. 750 objects have been merged with this code, resulting in objects with
  554. point, face, and edge counts in the mid-thousands (oh yes, and over
  555. 1400 subgroups!).  I really mean "merge" here, not group. 
  556.  
  557. The last EXTR object in the list defines the face-insensitive
  558. attributes like hardness, specular, etc. for the entire mega object.
  559. NOTE:  this implies that if you are merging two EXTR objects, one with
  560. a zero hardness, and one with a hardness of 255, then which ever is
  561. the last in the list before End MXTR defines the hardness for all.  In
  562. short, don't merge objects with vastly differing face-insensitive
  563. attributes (for example, chrome and rubber).
  564.  
  565. All component EXTR objects contribute to fill in the face-sensitive
  566. attributes (point, edge, face, color, reflect, and filter information)
  567. for the object (i.e. - all face-specific attributes are preserved for
  568. all faces of all component EXTR objects).  Yes, that includes all
  569. subgroups defined for all component EXTR objects.  Names of subgroups
  570. will be "mangled" with a counter to better let you identify who to
  571. change.  I considered merging subgroups (since they can build up
  572. fast), but figured that is best for a future enhancement. 
  573.  
  574. Face-sensitive "attributes" that were thrown out entirely are textures
  575. and brushmaps.  The reason is that these are limited to 4 per object.
  576. What do you do when you merge 1000 objects, each with three textures
  577. defined?  Which 4 textures do you use?  What should the texture axis
  578. positions be?  How about the texture scale factors?  In short, apply
  579. your textures to your merged object (using subgroups if you wish to
  580. restrict the scope) once you have loaded it into Imagine. 
  581.  
  582. Another limitation is that at present I do not support heirachical
  583. EXTR objects.  If you look at the length of the code below, I think
  584. you will understand why.  To do so, simply unroll the heirarchy into
  585. the master object.  This will require an additional preprocessing step
  586. to properly calculate point, edge, and face counts, and some recursion
  587. in the main copying loop.  NOTE:  By not preserve I mean two things:
  588. 1) at present I only copy the parent of an EXTR object that is
  589. actually a group.  2) I don't try to build heirarchy into the merged
  590. object either. 
  591.  
  592. Why not *preserve* heirachies, you ask?  Because Imagine reacts poorly
  593. to the creation of massive numbers of objects at a time.  Loading any
  594. more than 100-300 objects at a time into the detail results in a wait
  595. that can stretch on for hours as Imagine sets up for fast access to
  596. these objects.  The objects all happily draw in the tri-view window
  597. almost instantly, but the pointer remains BUSY for a *long* time.
  598. Once the BUSY pointer vanishes, Imagine draws the wireframe appears in
  599. the perspective window.   From that point on, access to all objects in
  600. the detail editor acts normally.
  601.  
  602. For example, adding 128 grouped objects in the detail editor (by a
  603. simple paste operation from a copy of an existing set of 128 grouped
  604. objects) resulted in a wait of over 5 minutes on a vanilla A3000 with
  605. 14M.  Over 11M remained free.  Loading 256 objects resulted in a wait
  606. of 15 minutes (over 9 M remained free).  These were 4 point, 6 edge, 2
  607. face objects.  The wait seems to go up as a high power of object
  608. complexity.  Loading 243 objects only twice as complex took longer
  609. than 1/2 - 3/4 hours, at which time I gave up and rebooted.   Nothing
  610. else was happening on the machine at the time.
  611.  
  612. Also, I wrote this code to allow the merging of massive numbers of
  613. *simple*, nearly identical objects.  You know, so I can merge blades
  614. of grass to make a lawn.  Or many individual leaves to make a leafy
  615. tree.  In this case, the above limitation clamps down on one fast.  If
  616. you want to merge only 40 EXTR objects, with a group of a parent and
  617. two children objects each - fine, write a GXTR chunk to handle this -
  618. 120 objects is managable, barely.  But any more than this and you
  619. should reconsider what you are trying to do. 
  620.  
  621. Hence, no groups of objects when merging (although you are free to
  622. change this - just don't break the existing functionality of MXTR)
  623.  
  624. 2)  An addition to process_OBJ(world):
  625.  
  626. */
  627.  
  628.                 /*--------------------------------------------\        
  629.                 |  Charles' magical EXTR object merge hack.   |
  630.                 \--------------------------------------------*/
  631.         } else if (strcmp(name, "MXTR")==0) {
  632.             p = process_MXTR(create_object(), world);
  633.             if (world->curobj) {
  634.                 world->curobj->next = p;
  635.                 p->parent = world->curobj->parent;
  636.             } else {
  637.                 world->object = p;
  638.                 p->parent = 0;
  639.             }
  640.             world->curobj = p;
  641.  
  642.                 /*---------------------------------------------------\        
  643.                 |  End of Charles' magical EXTR object merge hack.   |
  644.                 \---------------------------------------------------*/
  645.  
  646.         } else if (strcmp(name, "DESC")==0) {
  647.             p = create_object();
  648.             if (world->num_DESC > world->num_TOBJ+depth) {    /* This is a child */
  649.                 depth++;    /* Down one in the hierarchy */
  650.                 world->curobj->child = p;
  651.                 p->parent = world->curobj;
  652.             } else {
  653.                 if (world->curobj) {
  654.                     world->curobj->next = p;
  655.                     p->parent = world->curobj->parent;
  656.                 } else {
  657.                     world->object = p;
  658.                     p->parent = 0;
  659.                 }
  660.             }
  661.             world->curobj = p;
  662.             process_DESC(&p->desc, world);
  663.         } else if (strcmp(name, "TOBJ")==0) {
  664.             world->num_TOBJ++;
  665.             if (world->num_TOBJ > world->num_DESC) {
  666.                 warn(world->cur_line);
  667.                 fprintf(stderr, "TOBJ without DESC.  Ignored.\n");
  668.             }
  669.             if (world->num_TOBJ+depth > world->num_DESC) {    /* Go back up a level */
  670.                 depth--;
  671.                 world->curobj=world->curobj->parent;
  672.             }
  673.         } else if (strcmp(name, "END" )==0) {
  674.             if (world->num_DESC > world->num_TOBJ) {
  675.                 int i;
  676.                 warn(world->cur_line);
  677.                 i = world->num_DESC - world->num_TOBJ;
  678.                 fprintf(stderr, "Missing %d 'TOBJ'.  Inserted.\n", i);
  679.             }
  680.             parse_word(name, 4, 1);
  681.             if (strcmp(name, "OBJ")!=0)
  682.                 fprintf(stderr, "WARNING: Line %d: Expected 'END OBJ' but got: 'END %s'\n", world->cur_line, name);
  683.             parse_word(ps, 0, 0);    /* Get the quote, if any */
  684.             if (strcmp(this_level, ps)!=0)
  685.                 fprintf(stderr, "WARNING: Line %d: 'OBJ Begin' and 'End OBJ' quoted comments do not match\n", world->cur_line);
  686.             break;
  687.         } else if (verbose_flag)
  688.             fprintf(stderr, "WARNING: Line %d: Unknown OBJ sub-sub-chunk: '%s'\n",
  689.                 world->cur_line, name);
  690.     }
  691. }
  692.  
  693. /*---------------------------------------------------------\
  694. |  How to merge EXTR objects into a single mega object.    |
  695. \---------------------------------------------------------*/
  696. static OBJECT *process_MXTR(obj, world)
  697. OBJECT *obj;
  698. WORLD  *world;
  699. {    /* process_MXTR */
  700.         register OBJECT   *p, *base, *end;
  701.         register DESC     *desc, *edesc;
  702.         char              this_level[MAXLINE], name[5], nummy[8];
  703.         ULONG             pcount, ecount, fcount;
  704.         ULONG             pstart, estart, fstart;
  705.         ULONG             i;
  706.         XYZ_st            *crd, *ocrd;
  707.         ULONG             fcnt, ecnt, extrcount;
  708.         FGRP              *fgrp, *ofgrp;
  709.  
  710.         pcount = ecount = fcount = 0;
  711.         
  712.         /*---------------------------------------------------------------------\
  713.         |  Set up place-holder object - we store all EXTR objects in a linked  |
  714.         |  list, then merge into obj once we hit END MXTR.                     |
  715.         \---------------------------------------------------------------------*/
  716.         base = create_object();
  717.  
  718.         base->parent = (struct OBJECT *)0;
  719.         end = base;
  720.  
  721.         parse_word(this_level, 0, 1);   /* No limit, uppercase. */
  722.         if (strcmp(this_level, "BEGIN") != 0) {
  723.                 fprintf(stderr, "WARNING: Line %d: Bad syntax.  Missing 'Begin' after 'MXTR'\n",
  724.                         world->cur_line);
  725.         }
  726.         parse_word(this_level, 0, 0);   /* No limit, original case */
  727.         
  728.         /*---------------------------------------------------------------\      
  729.         |  Get EXTR objects to merge until END MXTR.  Any other chuck    |
  730.         |  ignored.                                                      |
  731.         \---------------------------------------------------------------*/
  732.  
  733.         while (get_line(strin, world)) {
  734.                 parse_word(name, 4, 1);         /* Get command */
  735.                 if   (strcmp(name, "EXTR")==0) {
  736.                    /*-----------------------------------------------------\             
  737.                    |  Read all EXTR objects that make up this "group".    |
  738.                    \-----------------------------------------------------*/
  739.                    p = process_EXTR(create_object(), world);
  740.                    pcount = pcount + p->desc->pcount;
  741.                    ecount = ecount + p->desc->ecount;
  742.                    fcount = fcount + p->desc->fcount;
  743.                    end->next = p;
  744.                    p->parent = end;
  745.                    end = p;
  746.                 } else if (strcmp(name, "END" )==0) {
  747.                   /*-------------------------------------\              
  748.                   |  End off this EXTR object series.    |
  749.                   \-------------------------------------*/
  750.                   parse_word(name, 4, 1);  
  751.                   if (strcmp(name, "MXTR")!=0)
  752.                      fprintf(stderr, "WARNING: Line %d: Expected 'END MXTR' but got: 'END %s'\n", world->cur_line, name);
  753.                   parse_word(ps, 0, 0); /* Get the quote, if any */
  754.                   if (strcmp(this_level, ps)!=0)
  755.                         fprintf(stderr, "WARNING: Line %d: 'MXTR Begin' and 'End MXTR' quoted comments do not match\n", world->cur_line);
  756.                   break;
  757.                 }
  758.         }   /* while MXTR */
  759.  
  760.  
  761.          /*-------------------------------------------------\   
  762.          |  OK, cap base object for our collection scan.    |
  763.          \-------------------------------------------------*/
  764.          end->next = (struct OBJECT *)0;
  765.  
  766.          end = base->next;   /* Return to first object */
  767.          base->next = (struct OBJECT *)0;  /* Since when it comes time to
  768.                                        free, this is all that will be left */
  769.          edesc = end->desc;
  770.          
  771.          /*----------------------------\
  772.          |  Set up our mega object.    |
  773.          \----------------------------*/
  774.          if (!(desc = (DESC*)malloc(sizeof(DESC)))) { OUT_MEM("DESC"); }
  775.          bzero((char*)desc, sizeof(DESC));
  776.          obj->desc = desc;
  777.  
  778.          /*-------------------------------------------------------\
  779.          |  Copy all attributes from 1st object in EXTR chain.    |
  780.          \-------------------------------------------------------*/
  781.          desc_copy(edesc, desc);        
  782.  
  783.          
  784.          /*------------------------\     
  785.          |  Set a few defaults.    |
  786.          \------------------------*/
  787.  
  788.          /* Set up defaults: */
  789.          defclst[0] = defclst[1] = defclst[2] = 240;    /* TS default */
  790.          defrlst[0] = defrlst[1] = defrlst[2] = 0;
  791.          deftlst[0] = deftlst[1] = deftlst[2] = 0;
  792.          defspc1[0] = defspc1[1] = defspc1[2] = 0;
  793.  
  794.          desc->pcount = pcount;
  795.          desc->ecount = ecount;
  796.  
  797.          /*----------------------------------------------------\
  798.          |  Allocate space for point, face, and edge arrays.   |
  799.          \----------------------------------------------------*/
  800.          if (!(desc->pnts = (XYZ_st*)malloc(desc->pcount*sizeof(XYZ_st))))
  801.                                 OUT_MEM("PNTS");
  802.          /* Initialize array */
  803.          for (i=0; i<desc->pcount; i++) {
  804.              desc->pnts[i].x = 0;
  805.              desc->pnts[i].y = 0;
  806.              desc->pnts[i].z = 0;
  807.          }
  808.  
  809.          if (!(desc->edge = (UWORD*)malloc(2*desc->ecount*sizeof(UWORD))))
  810.                                 OUT_MEM("EDGE");
  811.          /* Initialize array */
  812.          for (i=0; i<2*desc->ecount; i++)
  813.              desc->edge[i] = 0;
  814.  
  815.          malloc_arrays(fcount, desc);
  816.  
  817.          /*---------------------------------------------------------\
  818.          |  Now walk down objects, copying point, face, edge,       |
  819.          |  color, transparancy, and reflectivity information.      |
  820.          |  All other attributes have been stolen from the first    |
  821.          |  object.  Subobjects are copied                          |
  822.          |  POTENTIAL BUG:  I AM ASSUMING THE EXTR OBJECTS WE READ  |
  823.          |  HERE ARE NOT THEMSELVES HIERACHIES.   MIGHT NEED SOME   |
  824.          |  RECURSION, OR SOMETHING SIMILAR, IF THIS IS THE CASE.   |
  825.          \---------------------------------------------------------*/
  826.          ocrd = desc->pnts;
  827.          
  828.          pstart = estart = fstart = 0;
  829.          fcnt = ecnt = extrcount = 0;
  830.  
  831.          while (end != (struct OBJECT *)0)
  832.          {     /* For all objects to merge */
  833.             edesc = end->desc;
  834.             /*---------------------------------------------------------------\    
  835.             |  Copy points over - OK, I could use BCOPY, but I want control. |
  836.             \---------------------------------------------------------------*/
  837.             crd = edesc->pnts;
  838.             for (i = 0; i < edesc->pcount; i++)
  839.             {
  840.                ocrd->x = crd->x;
  841.                ocrd->y = crd->y;
  842.                ocrd->z = crd->z;
  843.                ocrd++;
  844.                crd++;
  845.             }
  846.             
  847.             /*---------------\      
  848.             |  Now edges.    |
  849.             \---------------*/
  850.             
  851.             for (i=0; i<2*edesc->ecount; i++)
  852.             {
  853.                desc->edge[ecnt] = edesc->edge[i] + pstart;
  854.                ecnt++;
  855.             }
  856.                
  857.             /*----------------------------------------------\       
  858.             |  And the faces and their basic attributes.    |
  859.             \----------------------------------------------*/
  860.             for (i = 0; i<3*edesc->fcount; i+=3)
  861.             {
  862.                desc->face[fcnt]   = edesc->face[i] + estart;
  863.                desc->face[fcnt+1] = edesc->face[i+1] + estart;
  864.                desc->face[fcnt+2] = edesc->face[i+2] + estart;
  865.                desc->clst[fcnt]   = edesc->clst[i];
  866.                desc->clst[fcnt+1] = edesc->clst[i+1];
  867.                desc->clst[fcnt+2] = edesc->clst[i+2];
  868.                desc->rlst[fcnt]   = edesc->rlst[i];
  869.                desc->rlst[fcnt+1] = edesc->rlst[i+1];
  870.                desc->rlst[fcnt+2] = edesc->rlst[i+2];
  871.                desc->tlst[fcnt]   = edesc->tlst[i];
  872.                desc->tlst[fcnt+1] = edesc->tlst[i+1];
  873.                desc->tlst[fcnt+2] = edesc->tlst[i+2];
  874.                fcnt+=3;
  875.             }
  876.             
  877.             /*----------------------------------\           
  878.             |  And the subgroup definitions.    |
  879.             \----------------------------------*/
  880.             if (edesc->fgrp)
  881.             {       /* Do subgroups */
  882.                fgrp = edesc->fgrp;
  883.                while (fgrp != (FGRP *) 0)
  884.                {    /* While subgroups to copy */
  885.                   /*------------------------\       
  886.                   |  Get FGRP structure.    |
  887.                   \------------------------*/
  888.                   if (!(ofgrp = (FGRP*)malloc(sizeof(FGRP))))
  889.                                OUT_MEM("FGRP");
  890.                   /*------------------------------\
  891.                   |  Fill out name and number.    |
  892.                   \------------------------------*/
  893.                   bzero((char *)ofgrp, sizeof(FGRP));
  894.                   
  895.                   ofgrp->num = fgrp->num;
  896.                   
  897.                   /*-----------------------------------------------\              
  898.                   |  Mangle name so unique for each subobject of   |
  899.                   |  each component EXTR object.                   |
  900.                   \-----------------------------------------------*/
  901.                   i = strlen(fgrp->name);
  902.                   if (i > 13)   /* The name field is 18 characters  */
  903.                      i = 13;    /* tops, and we remove 5 characters for
  904.                                        the ASCII representations of 0-65535 */
  905.                   strncpy(ofgrp->name, fgrp->name, i);
  906.                   ofgrp->name[i] = '\0';   /* Arrays start at 0 */
  907.                   sprintf(nummy, "%d", extrcount);
  908.                   strcat(ofgrp->name, nummy);   /* uniquely tag subgroup
  909.                                                    name by number of parent
  910.                                                    EXTR object */
  911.                   
  912.                   /*-----------------------------\                
  913.                   |  Insert into linked list.    |
  914.                   \-----------------------------*/
  915.                   ofgrp->next = desc->fgrp;
  916.                   desc->fgrp = ofgrp;
  917.                      
  918.                   /*-----------------------------------\                  
  919.                   |  And copy in the face indicies.    |
  920.                   \-----------------------------------*/
  921.                   if (!(ofgrp->face = 
  922.                            (UWORD *)malloc((fgrp->num)*sizeof(UWORD))))
  923.                                         OUT_MEM("FGRP");
  924.                   for (i = 0; i < fgrp->num; i++)
  925.                   {
  926.                      ofgrp->face[i] = (UWORD) (fgrp->face[i] + fstart);
  927.                   }
  928.                   
  929.                   fgrp = fgrp->next;
  930.                }    /* While subgroups to copy */
  931.             }       /* Do subgroups */
  932.  
  933.             /*-------------------------------------------------------\      
  934.             |  Copying textures in a similar manner would be         |
  935.             |  rather difficult, since we can only have 4 textures   |
  936.             |  per object.  I suggest having the user make their     |
  937.             |  own subgroups and apply the texture to the merged     |
  938.             |  object, since we run into too many difficulties if    |
  939.             |  we try to do this for them here.  There is a time     |
  940.             |  and a place for everything, and I think textures      |
  941.             |  don't belong here.                                    |
  942.             \-------------------------------------------------------*/
  943.  
  944.             /*----------------------------\         
  945.             |  Set up for next object.    |
  946.             \----------------------------*/
  947.             pstart = pstart + edesc->pcount;
  948.             estart = estart + edesc->ecount;
  949.             fstart = fstart + edesc->fcount;
  950.             extrcount++;
  951.             p = end;
  952.             end = end->next;
  953.  
  954.             /*--------------------------------------------------\           
  955.             |  And free object now that we are done with it.    |
  956.             \--------------------------------------------------*/
  957.             p->next = (struct OBJECT *)0;   /* Delete only this guy in chain */
  958.             p->parent = (struct OBJECT *)0;
  959.             free_object(p);
  960.          }       /* For all objects to merge */
  961.  
  962.          /*---------------------\
  963.          |  Free base object.   |
  964.          \---------------------*/
  965.          free_object(base);
  966.          
  967.          /*-------------------------\    
  968.          |  And return in glory.    |
  969.          \-------------------------*/
  970.          return(obj);
  971.  
  972. }    /* process_MXTR */
  973.  
  974. /*-------------------------------------------------------------------\
  975. |  Copy an object's attributes from one desc structure to another.   |
  976. |  What a pain.  But someone had to do it...                         |
  977. \-------------------------------------------------------------------*/
  978. static void desc_copy(edesc, desc)
  979. DESC *edesc, /* input desc structure */
  980.      *desc;  /* output desc structure */
  981. {     /* desc_copy */
  982.    if (edesc->name != '\0')
  983.       strcpy(desc->name, edesc->name);
  984.  
  985.    if (edesc->eflg)
  986.    {
  987.       if (!(desc->eflg = (EFLG*)malloc(sizeof(EFLG))))
  988.                         OUT_MEM("EFLG");
  989.       bcopy((char *)edesc->eflg, (char *)desc->eflg, sizeof(EFLG));
  990.       if (edesc->eflg->eflg)
  991.       {
  992.          if (!(desc->eflg->eflg = 
  993.               (UBYTE*)malloc(desc->eflg->num*sizeof(UBYTE))))
  994.                     OUT_MEM("EFLG");
  995.          bcopy((char *)edesc->eflg->eflg, (char *)edesc->eflg->eflg, 
  996.                edesc->eflg->num*sizeof(UBYTE));
  997.       }
  998.    }
  999.                  
  1000.    if (edesc->shap)
  1001.    {
  1002.       if (!(desc->shap=(WORD*)malloc(2*sizeof(WORD))))
  1003.                                         { OUT_MEM("SHAP"); }
  1004.       bcopy((char *)edesc->shap, (char *)desc->shap, 2*sizeof(WORD));
  1005.    }
  1006.          
  1007.    if (edesc->posi)
  1008.    {
  1009.       if (!(desc->posi=(XYZ_st*)malloc(sizeof(XYZ_st))))
  1010.                                         { OUT_MEM("POSI"); }
  1011.       bcopy((char *)edesc->posi, (char *)desc->posi, sizeof(XYZ_st));
  1012.    }
  1013.  
  1014.    if (edesc->axis)
  1015.    {
  1016.       if (!(desc->axis=(AXIS*)malloc(sizeof(AXIS))))
  1017.                 { OUT_MEM("AXIS"); }
  1018.       bcopy((char *)edesc->axis, (char *)desc->axis, sizeof(AXIS));
  1019.    }
  1020.    
  1021.    if (edesc->size)
  1022.    {
  1023.       if (!(desc->size=(XYZ_st*)malloc(sizeof(XYZ_st))))
  1024.                         { OUT_MEM("SIZE"); }
  1025.       bcopy((char *)edesc->size, (char *)desc->size, sizeof(XYZ_st));
  1026.    }
  1027.  
  1028.    if (edesc->colr)
  1029.    {
  1030.       if (!(desc->colr = (RGB_st*)malloc(sizeof(RGB_st))))
  1031.                                         { OUT_MEM("COLR"); }
  1032.       bcopy((char *)edesc->colr, (char *)desc->colr, sizeof(RGB_st));
  1033.    }
  1034.    
  1035.    if (edesc->refl)
  1036.    {
  1037.       if (!(desc->refl = (RGB_st*)malloc(sizeof(RGB_st))))
  1038.                                         { OUT_MEM("REFL"); }
  1039.       bcopy((char *)edesc->refl, (char *)desc->refl, sizeof(RGB_st));
  1040.    }
  1041.  
  1042.    if (edesc->tran)
  1043.    {
  1044.       if (!(desc->tran = (RGB_st*)malloc(sizeof(RGB_st))))
  1045.                                         { OUT_MEM("TRAN"); }
  1046.       bcopy((char *)edesc->tran, (char *)desc->tran, sizeof(RGB_st));
  1047.    }
  1048.  
  1049.    if (edesc->spc1)
  1050.    {
  1051.       if (!(desc->spc1 = (RGB_st*)malloc(sizeof(RGB_st))))
  1052.                                         { OUT_MEM("SPC1"); }
  1053.       bcopy((char *)edesc->spc1, (char *)desc->spc1, sizeof(RGB_st));
  1054.    }
  1055.  
  1056.    if (edesc->tpar)
  1057.    {
  1058.       if (!(desc->tpar=(double*)malloc(16*sizeof(double))))
  1059.                                         { OUT_MEM("TPAR"); }
  1060.       bcopy((char *)edesc->tpar, (char *)desc->tpar, 16*sizeof(double));
  1061.    }
  1062.  
  1063.    if (edesc->surf)
  1064.    {
  1065.       if (!(desc->surf=(UBYTE*)malloc(5*sizeof(UBYTE))))
  1066.                                         { OUT_MEM("SURF"); }
  1067.       bcopy((char*)edesc->surf, (char *)desc->surf, 5*sizeof(UBYTE));
  1068.    }
  1069.  
  1070.    if (edesc->mttr)
  1071.    {
  1072.       if (!(desc->mttr=(MTTR*)malloc(sizeof(MTTR))))
  1073.                                         { OUT_MEM("MTTR"); }
  1074.       bcopy((char*)edesc->mttr, (char *)desc->mttr, sizeof(MTTR));
  1075.    }
  1076.    
  1077.    if (edesc->spec)
  1078.    {
  1079.       if (!(desc->spec=(UBYTE*)malloc(2*sizeof(UBYTE))))
  1080.                                         { OUT_MEM("SPEC"); }
  1081.       bcopy((char*)edesc->spec, (char *)desc->spec, 2*sizeof(UBYTE));
  1082.    }
  1083.  
  1084.    if (edesc->prp0)
  1085.    {
  1086.       if (!(desc->prp0=(UBYTE*)malloc(6*sizeof(UBYTE))))
  1087.                                         { OUT_MEM("PRP0"); }
  1088.       bcopy((char*)edesc->prp0, (char *)desc->prp0, 6*sizeof(UBYTE));
  1089.    }
  1090.  
  1091.    if (edesc->prp1)
  1092.    {
  1093.       if (!(desc->prp1=(UBYTE*)malloc(8*sizeof(UBYTE))))
  1094.                                         { OUT_MEM("PRP1"); }
  1095.       bcopy((char*)edesc->prp1, (char *)desc->prp1, 8*sizeof(UBYTE));
  1096.    }
  1097.  
  1098.    if (edesc->ints)
  1099.    {
  1100.       if (!(desc->ints=(double*)malloc(sizeof(double))))
  1101.                                         { OUT_MEM("INTS"); }
  1102.       bcopy((char *)edesc->ints, (char *)desc->ints, sizeof(double));
  1103.    }
  1104.    
  1105.    if (edesc->int1)
  1106.    {
  1107.       if (!(desc->int1=(XYZ_st*)malloc(sizeof(XYZ_st))))
  1108.                                    { OUT_MEM("INT1"); }
  1109.       bcopy((char *)edesc->int1, (char *)desc->int1, sizeof(XYZ_st));
  1110.    }
  1111. }     /* desc_copy */
  1112. /*----------------------\
  1113. |  That's all folks.    |
  1114. \----------------------*/
  1115.  
  1116. static OBJECT *process_EXTR(obj, world)
  1117. OBJECT *obj;
  1118. WORLD *world;
  1119. {
  1120.     register EXTR *extr;
  1121.     register OBJECT *p;
  1122.     char this_level[MAXLINE], name[5];
  1123.     MTRX *mtrx;
  1124.     WORLD *new;
  1125.     FILE *newinp;
  1126.  
  1127.     if (!(extr = obj->extr = (EXTR*)malloc(sizeof(EXTR)))) { OUT_MEM("EXTR"); }
  1128.     bzero((char*)extr, sizeof(EXTR));
  1129.     mtrx = &extr->mtrx;
  1130.     /* Initialize structure */
  1131.     mtrx->tran.x  = mtrx->tran.y  = mtrx->tran.z  = 0.0;
  1132.     mtrx->scal.x  = mtrx->scal.y  = mtrx->scal.z  = 1.0;
  1133.     mtrx->rota1.y = mtrx->rota1.z = 0.0;
  1134.     mtrx->rota2.x = mtrx->rota2.z = 0.0;
  1135.     mtrx->rota3.x = mtrx->rota3.y = 0.0;
  1136.     mtrx->rota1.x = mtrx->rota2.y = mtrx->rota3.z = 1.0;
  1137.  
  1138.     parse_word(this_level, 0, 1);    /* No limit, uppercase. */
  1139.     if (strcmp(this_level, "BEGIN") != 0) {
  1140.         fprintf(stderr, "WARNING: Line %d: Bad syntax.  Missing 'Begin' after 'EXTR'\n",
  1141.             world->cur_line);
  1142.     }
  1143.     parse_word(this_level, 0, 0);    /* No limit, original case */
  1144.  
  1145.     while (get_line(strin, world)) {
  1146.         parse_word(name, 4, 1);        /* Get command */
  1147.         if        (strcmp(name, "MTRX")==0) {
  1148.             parse_word(name, 4, 1);        /* Get field */
  1149.             if      (strcmp(name, "TRAN")==0) stuff_XYZ(&mtrx->tran);
  1150.             else if (strcmp(name, "SCAL")==0) stuff_XYZ(&mtrx->scal);
  1151.             else if (strcmp(name, "ROTA")==0) {
  1152.                 stuff_XYZ(&mtrx->rota1);
  1153.                 stuff_XYZ(&mtrx->rota2);
  1154.                 stuff_XYZ(&mtrx->rota3);
  1155.             } else {
  1156.                 if (verbose_flag)
  1157.                     fprintf(stderr, "WARNING: Line %d: Unknown MTRX field: '%s'\n",
  1158.                         world->cur_line, name);
  1159.                 continue;
  1160.             }
  1161.         } else if (strcmp(name, "LOAD")==0) {
  1162.             parse_word(extr->filename, 80, 0);
  1163.         } else if (strcmp(name, "END" )==0) {
  1164.             parse_word(name, 4, 1);
  1165.             if (strcmp(name, "EXTR")!=0)
  1166.                 fprintf(stderr, "WARNING: Line %d: Expected 'END EXTR' but got: 'END %s'\n", world->cur_line, name);
  1167.             parse_word(ps, 0, 0);    /* Get the quote, if any */
  1168.             if (strcmp(this_level, ps)!=0)
  1169.                 fprintf(stderr, "WARNING: Line %d: 'EXTR Begin' and 'End EXTR' quoted comments do not match\n", world->cur_line);
  1170.             break;
  1171.         } else if (verbose_flag)
  1172.             fprintf(stderr, "WARNING: Line %d: Unknown EXTR sub-sub-chunk: '%s'\n",
  1173.                 world->cur_line, name);
  1174.     }
  1175.     /* Now, load in the external object */
  1176.     if (!(newinp=fopen(extr->filename, "r"))) {
  1177.         fprintf(stderr, "Can't load in EXTR object: '%s'... ignored.\n",
  1178.             extr->filename);
  1179.         return(obj);
  1180.     }
  1181.     new = read_World(newinp);
  1182.     fclose(newinp);
  1183.     /* scale, rotate, and translate new object hierarchy */
  1184.     for (p=new->object; p; p=p->next)
  1185.         move_extr(p, mtrx);
  1186.     /* Free up unused memory */
  1187.     free((char*)obj->extr);
  1188.     free((char*)obj);
  1189.     obj = new->object;
  1190.     free((char*)new);
  1191.     return(obj);
  1192. }
  1193.  
  1194. void move_extr(obj, mtrx)
  1195. register OBJECT *obj;
  1196. register MTRX *mtrx;
  1197. {
  1198.     register XYZ_st *p;
  1199.     register int i;
  1200.     register double x, y, z;
  1201.     if (obj->desc) {
  1202.         if (obj->desc->posi) {
  1203.             obj->desc->posi->x += mtrx->tran.x;
  1204.             obj->desc->posi->y += mtrx->tran.y;
  1205.             obj->desc->posi->z += mtrx->tran.z;
  1206.         }
  1207.         for (p=obj->desc->pnts,i=obj->desc->pcount; i--; p++) {
  1208.             x = (p->x*mtrx->scal.x);
  1209.             y = (p->y*mtrx->scal.y);
  1210.             z = (p->z*mtrx->scal.z);
  1211.             p->x = x*mtrx->rota1.x +
  1212.                         y*mtrx->rota1.y +
  1213.                         z*mtrx->rota1.z +
  1214.                         mtrx->tran.x;
  1215.             p->y = x*mtrx->rota2.x +
  1216.                         y*mtrx->rota2.y +
  1217.                         z*mtrx->rota2.z +
  1218.                         mtrx->tran.y;
  1219.             p->z = x*mtrx->rota3.x +
  1220.                         y*mtrx->rota3.y +
  1221.                         z*mtrx->rota3.z +
  1222.                         mtrx->tran.z;
  1223.         }
  1224.         if (obj->desc->size) {
  1225.             obj->desc->size->x = mtrx->scal.x;
  1226.             obj->desc->size->y = mtrx->scal.y;
  1227.             obj->desc->size->z = mtrx->scal.z;
  1228.         }
  1229.     }
  1230.     for (obj=obj->child; obj; obj=obj->next)
  1231.         move_extr(obj, mtrx);    /* Process all children */
  1232. }
  1233.  
  1234. void OUT_MEM(s)
  1235. char *s;
  1236. {
  1237.     if (s)
  1238.     fprintf(stderr, "Ran out of memory while processing '%s'.  Sorry.\n", s);
  1239.     exit(-1);
  1240. }
  1241.  
  1242. static void malloc_arrays(i, desc)
  1243. register int i;
  1244. register DESC *desc;
  1245. {
  1246.     if (!desc->fcount) {
  1247.         desc->fcount = i;
  1248. if (!(desc->face=(UWORD*)malloc(3*desc->fcount*sizeof(UWORD))))OUT_MEM((char*)0);
  1249. if (!(desc->clst=(UBYTE*)malloc(3*desc->fcount*sizeof(UBYTE))))OUT_MEM((char*)0);
  1250. if (!(desc->rlst=(UBYTE*)malloc(3*desc->fcount*sizeof(UBYTE))))OUT_MEM((char*)0);
  1251. if (!(desc->tlst=(UBYTE*)malloc(3*desc->fcount*sizeof(UBYTE))))OUT_MEM((char*)0);
  1252.         /* Initialize arrays */
  1253.         for (i=0; i<3*desc->fcount; i+=3) {
  1254.             desc->face[i]   = desc->face[i+1] = desc->face[i+2] = 0;
  1255.             desc->clst[i]   = defclst[0];
  1256.             desc->clst[i+1] = defclst[1];
  1257.             desc->clst[i+2] = defclst[2];
  1258.             desc->rlst[i]   = defrlst[0];
  1259.             desc->rlst[i+1] = defrlst[1];
  1260.             desc->rlst[i+2] = defrlst[2];
  1261.             desc->tlst[i]   = deftlst[0];
  1262.             desc->tlst[i+1] = deftlst[1];
  1263.             desc->tlst[i+2] = deftlst[2];
  1264.         }
  1265.     } else if (i != desc->fcount) {
  1266.         fprintf(stderr, "ERROR: FACE and [C|R|T]LST 'Count' values inconsistant.\n");
  1267.         OUT_MEM((char*)0);
  1268.     }
  1269. }
  1270.  
  1271. static void process_DESC(orig, world)
  1272. DESC **orig;
  1273. WORLD *world;
  1274. {
  1275.     register DESC *desc;
  1276.     register int i, j;
  1277.     char this_level[MAXLINE], name[5];
  1278.     FGRP *fgrp;
  1279.  
  1280.     if (!(desc = *orig = (DESC*)malloc(sizeof(DESC)))) { OUT_MEM("DESC"); }
  1281.     bzero((char*)desc, sizeof(DESC));
  1282.  
  1283.     /* Set up defaults: */
  1284.     defclst[0] = defclst[1] = defclst[2] = 240;    /* TS default */
  1285.     defrlst[0] = defrlst[1] = defrlst[2] = 0;
  1286.     deftlst[0] = deftlst[1] = deftlst[2] = 0;
  1287.     defspc1[0] = defspc1[1] = defspc1[2] = 0;
  1288.  
  1289.     parse_word(this_level, 0, 1);    /* No limit, uppercase. */
  1290.     if (strcmp(this_level, "BEGIN") != 0) {
  1291.         fprintf(stderr, "WARNING: Line %d: Bad syntax.  Missing 'Begin' after 'DESC'\n",
  1292.             world->cur_line);
  1293.     }
  1294.     parse_word(this_level, 0, 0);    /* No limit, original case */
  1295.  
  1296.     world->num_DESC++;
  1297.  
  1298.     while (get_line(strin, world)) {
  1299.         parse_word(name, 4, 1);        /* Get command */
  1300.         /* Put most-frequent commands near the front of this list */
  1301.         if (strcmp(name, "PNTS")==0) {
  1302.             parse_word(name, 4, 1);        /* Get field */
  1303.             if          (strcmp(name, "POIN")==0) {
  1304.                 if (!desc->pcount) {
  1305.                     fprintf(stderr, "ERROR: Line %d: 'PNTS Point' encountered before 'PNTS PCount'\n", world->cur_line);
  1306.                     OUT_MEM((char*)0);
  1307.                 }
  1308.                 i = get_UWORD();
  1309.                 if (i<0 || i>=desc->pcount) {
  1310.                     fprintf(stderr, "WARNING: Line %d: Bad index: PNTS Point[%d] should be [0..%d]\n", world->cur_line, i, desc->pcount-1);
  1311.                     continue;
  1312.                 }
  1313.                 stuff_XYZ(&desc->pnts[i]);
  1314.             } else if (strcmp(name, "PCOU")==0) {
  1315.                 if (!desc->pcount) {
  1316.                     desc->pcount = get_UWORD();
  1317.                     if (!(desc->pnts = (XYZ_st*)malloc(desc->pcount*sizeof(XYZ_st))))
  1318.                         OUT_MEM("PNTS");
  1319.                     /* Initialize array */
  1320.                     for (i=0; i<desc->pcount; i++) {
  1321.                         desc->pnts[i].x = 0;
  1322.                         desc->pnts[i].y = 0;
  1323.                         desc->pnts[i].z = 0;
  1324.                     }
  1325.                 } else {
  1326.                     fprintf(stderr, "WARNING: Line %d: PNTS Pcount defined more than once.\n", world->cur_line);
  1327.                 }
  1328.             } else {
  1329.                 if (verbose_flag)
  1330.                     fprintf(stderr, "WARNING: Line %d: Unknown PNTS field: '%s'\n",
  1331.                         world->cur_line, name);
  1332.                 continue;
  1333.             }
  1334.         } else
  1335.         if (strcmp(name, "EDGE")==0) {
  1336.             parse_word(name, 4, 1);        /* Get field */
  1337.             if          (strcmp(name, "EDGE")==0) {
  1338.                 if (!desc->ecount) {
  1339.                     fprintf(stderr, "ERROR: Line %d: 'EDGE Edge' encountered before 'EDGE ECount'\n", world->cur_line);
  1340.                     OUT_MEM((char*)0);
  1341.                 }
  1342.                 i = get_UWORD();
  1343.                 if (i<0 || i>=desc->ecount) {
  1344.                     fprintf(stderr, "WARNING: Line %d: Bad index: EDGE Edge[%d] should be [0..%d]\n", world->cur_line, i, desc->ecount-1);
  1345.                     continue;
  1346.                 }
  1347.                 desc->edge[2*i]   = get_UWORD();
  1348.                 desc->edge[2*i+1] = get_UWORD();
  1349.             } else if (strcmp(name, "ECOU")==0) {
  1350.                 if (!desc->ecount) {
  1351.                     desc->ecount = get_UWORD();
  1352.                     if (!(desc->edge = (UWORD*)malloc(2*desc->ecount*sizeof(UWORD))))
  1353.                         OUT_MEM("EDGE");
  1354.                     /* Initialize array */
  1355.                     for (i=0; i<2*desc->ecount; i++)
  1356.                         desc->edge[i] = 0;
  1357.                 } else {
  1358.                     fprintf(stderr, "WARNING: Line %d: EDGE Ecount defined more than once.\n", world->cur_line);
  1359.                 }
  1360.             } else {
  1361.                 if (verbose_flag)
  1362.                     fprintf(stderr, "WARNING: Line %d: Unknown EDGE field: '%s'\n",
  1363.                         world->cur_line, name);
  1364.                 continue;
  1365.             }
  1366.         } else
  1367.         if (strcmp(name, "EFLG")==0) {
  1368.             parse_word(name, 4, 1);        /* Get field */
  1369.             if (strcmp(name, "COUN")==0) {
  1370.                 if (!desc->eflg) {
  1371.                     if (!(desc->eflg = (EFLG*)malloc(sizeof(EFLG))))
  1372.                         OUT_MEM("EFLG");
  1373.                     desc->eflg->num = get_UWORD();
  1374.                     if (!(desc->eflg->eflg = (UBYTE*)malloc(desc->eflg->num*sizeof(UBYTE))))
  1375.                         OUT_MEM("EFLG");
  1376.                     /* Initialize array */
  1377.                     for (i=0; i<desc->eflg->num; i++)
  1378.                         desc->eflg->eflg[i] = 0;
  1379.                 } else {
  1380.                     fprintf(stderr, "WARNING: Line %d: EFLG Count defined more than once.\n", world->cur_line);
  1381.                 }
  1382.             } else if (strcmp(name, "EFLG")==0) {
  1383.                 if (!desc->eflg) {
  1384.                     fprintf(stderr, "ERROR: Line %d: 'EFLG Eflg' encountered before 'EFLG Count'\n", world->cur_line);
  1385.                     OUT_MEM((char*)0);
  1386.                 }
  1387.                 i = get_UWORD();
  1388.                 if (i<0 || i>=desc->eflg->num) {
  1389.                     fprintf(stderr, "WARNING: Line %d: Bad index: EFLG Eflg[%d] should be [0..%d]\n", world->cur_line, i, desc->eflg->num-1);
  1390.                     continue;
  1391.                 }
  1392.                 desc->eflg->eflg[i]   = get_UWORD();
  1393.             } else {
  1394.                 if (verbose_flag)
  1395.                     fprintf(stderr, "WARNING: Line %d: Unknown EFLG field: '%s'\n",
  1396.                         world->cur_line, name);
  1397.                 continue;
  1398.             }
  1399.         } else
  1400.         if (strcmp(name, "FACE")==0) {
  1401.             parse_word(name, 4, 1);        /* Get field */
  1402.             if          (strcmp(name, "CONN")==0) {
  1403.                 if (!desc->fcount) {
  1404.                     fprintf(stderr, "ERROR: Line %d: 'FACE Connects' encountered before 'FACE TCount'\n", world->cur_line);
  1405.                     OUT_MEM((char*)0);
  1406.                 }
  1407.                 i = get_UWORD();
  1408.                 if (i<0 || i>=desc->fcount) {
  1409.                     fprintf(stderr, "WARNING: Line %d: Bad index: FACE Connects[%d] should be [0..%d]\n", world->cur_line, i, desc->fcount-1);
  1410.                     continue;
  1411.                 }
  1412.                 desc->face[3*i]   = get_UWORD();
  1413.                 desc->face[3*i+1] = get_UWORD();
  1414.                 desc->face[3*i+2] = get_UWORD();
  1415.             } else if (strcmp(name, "TCOU")==0) {
  1416.                 i = get_UWORD();
  1417.                 malloc_arrays(i, desc);
  1418.             } else {
  1419.                 if (verbose_flag)
  1420.                     fprintf(stderr, "WARNING: Line %d: Unknown FACE field: '%s'\n",
  1421.                         world->cur_line, name);
  1422.                 continue;
  1423.             }
  1424.         } else
  1425.         if (strcmp(name, "FGRP")==0) {
  1426.             parse_word(name, 4, 1);
  1427.             if (strcmp(name, "NAME")==0) {
  1428.                 if (!(fgrp = (FGRP*)malloc(sizeof(FGRP))))
  1429.                     OUT_MEM("FGRP");
  1430.                 bzero((char*)fgrp, sizeof(FGRP));
  1431.                 parse_word(ps, 0, 0);
  1432.                 strncpy(fgrp->name, ps, 18);
  1433.                 fgrp->name[18]='\0';
  1434.                 fgrp->next = desc->fgrp;
  1435.                 desc->fgrp = fgrp;
  1436.             } else if (strcmp(name, "COUN")==0) {
  1437.                 i = get_UWORD();
  1438.                 if (!desc->fgrp) {
  1439.                     fprintf(stderr, "ERROR: Line %d: 'FGRP Count' encountered before 'FGRP Name'\n", world->cur_line);
  1440.                     OUT_MEM((char*)0);
  1441.                 }
  1442.                 if (desc->fgrp->face) {
  1443.                     fprintf(stderr, "ERROR: Line %d: 'FGRP Count' encountered twice\n", world->cur_line);
  1444.                     OUT_MEM((char*)0);
  1445.                 }
  1446.                 desc->fgrp->num = i;
  1447.                 if (!(desc->fgrp->face = (UWORD*)malloc(i*sizeof(UWORD))))
  1448.                     OUT_MEM("FGRP");
  1449.                 while (i--) desc->fgrp->face[i] = 0;
  1450.             } else if (strcmp(name, "FACE")==0) {
  1451.                 i = get_UWORD();
  1452.                 if (i<0 || i>=desc->fgrp->num) {
  1453.                     fprintf(stderr, "WARNING: Line %d: Bad index: FGRP Face[%d] should be [0..%d]\n", world->cur_line, i, desc->fgrp->num-1);
  1454.                     continue;
  1455.                 }
  1456.                 desc->fgrp->face[i] = get_UWORD();
  1457.             } else {
  1458.                 if (verbose_flag)
  1459.                     fprintf(stderr, "WARNING: Line %d: Unknown FGRP field: '%s'\n",
  1460.                         world->cur_line, name);
  1461.                 continue;
  1462.             }
  1463.         } else
  1464.         if (strcmp(name, "CLST")==0) {
  1465.             parse_word(name, 4, 1);        /* Get field */
  1466.             if          (strcmp(name, "COLO")==0) {
  1467.                 if (!desc->fcount) {
  1468.                     fprintf(stderr, "ERROR: Line %d: 'CLST Color' encountered before 'CLST Count'\n", world->cur_line);
  1469.                     OUT_MEM((char*)0);
  1470.                 }
  1471.                 i = get_UWORD();
  1472.                 if (i<0 || i>=desc->fcount) {
  1473.                     fprintf(stderr, "WARNING: Line %d: Bad index: CLST Color[%d] should be [0..%d]\n", world->cur_line, i, desc->fcount-1);
  1474.                     continue;
  1475.                 }
  1476.                 stuff_RGB((RGB_st*)&desc->clst[3*i]);    /* Place 3 UBYTE's into array */
  1477.             } else if (strcmp(name, "COUN")==0) {
  1478.                 i = get_UWORD();
  1479.                 malloc_arrays(i, desc);
  1480.             } else {
  1481.                 if (verbose_flag)
  1482.                     fprintf(stderr, "WARNING: Line %d: Unknown CLST field: '%s'\n",
  1483.                         world->cur_line, name);
  1484.                 continue;
  1485.             }
  1486.         } else
  1487.         if (strcmp(name, "RLST")==0) {
  1488.             parse_word(name, 4, 1);        /* Get field */
  1489.             if          (strcmp(name, "COLO")==0) {
  1490.                 if (!desc->fcount) {
  1491.                     fprintf(stderr, "ERROR: Line %d: 'RLST Color' encountered before 'RLST Count'\n", world->cur_line);
  1492.                     OUT_MEM((char*)0);
  1493.                 }
  1494.                 i = get_UWORD();
  1495.                 if (i<0 || i>=desc->fcount) {
  1496.                     fprintf(stderr, "WARNING: Line %d: Bad index: RLST Color[%d] should be [0..%d]\n", world->cur_line, i, desc->fcount-1);
  1497.                     continue;
  1498.                 }
  1499.                 stuff_RGB((RGB_st*)&desc->rlst[3*i]);    /* Place three UBYTE's into array */
  1500.             } else if (strcmp(name, "COUN")==0) {
  1501.                 i = get_UWORD();
  1502.                 malloc_arrays(i, desc);
  1503.             } else {
  1504.                 if (verbose_flag)
  1505.                     fprintf(stderr, "WARNING: Line %d: Unknown RLST field: '%s'\n",
  1506.                         world->cur_line, name);
  1507.                 continue;
  1508.             }
  1509.         } else
  1510.         if (strcmp(name, "TLST")==0) {
  1511.             parse_word(name, 4, 1);        /* Get field */
  1512.             if          (strcmp(name, "COLO")==0) {
  1513.                 if (!desc->fcount) {
  1514.                     fprintf(stderr, "ERROR: Line %d: 'TLST Color' encountered before 'TLST Count'\n", world->cur_line);
  1515.                     OUT_MEM((char*)0);
  1516.                 }
  1517.                 i = get_UWORD();
  1518.                 if (i<0 || i>=desc->fcount) {
  1519.                     fprintf(stderr, "WARNING: Line %d: Bad index: TLST Color[%d] should be [0..%d]\n", world->cur_line, i, desc->fcount-1);
  1520.                     continue;
  1521.                 }
  1522.                 stuff_RGB((RGB_st*)&desc->tlst[3*i]);    /* Place three UBYTE's into array */
  1523.             } else if (strcmp(name, "COUN")==0) {
  1524.                 i = get_UWORD();
  1525.                 malloc_arrays(i, desc);
  1526.             } else {
  1527.                 if (verbose_flag)
  1528.                     fprintf(stderr, "WARNING: Line %d: Unknown TLST field: '%s'\n",
  1529.                         world->cur_line, name);
  1530.                 continue;
  1531.             }
  1532.         } else
  1533.         if (strcmp(name, "NAME")==0) {
  1534.             parse_word(ps, 0, 0);
  1535.             strncpy(desc->name, ps, 18);
  1536.             desc->name[18]='\0';
  1537.         } else
  1538.         if (strcmp(name, "SHAP")==0) {
  1539.             if (!desc->shap) {
  1540.                 if (!(desc->shap=(WORD*)malloc(2*sizeof(WORD))))
  1541.                     { OUT_MEM("SHAP"); }
  1542.                 desc->shap[0] = 2;    /* TS defaults */
  1543.                 desc->shap[1] = 0;
  1544.             }
  1545.             parse_word(name, 4, 1);        /* Get field */
  1546.             if        (strcmp(name, "SHAP")==0) desc->shap[0] = get_UWORD();
  1547.             else if (strcmp(name, "LAMP")==0) desc->shap[1] = get_UWORD();
  1548.             else {
  1549.                 if (verbose_flag)
  1550.                     fprintf(stderr, "WARNING: Line %d: Unknown SHAP field: '%s'\n",
  1551.                         world->cur_line, name);
  1552.                 continue;
  1553.             }
  1554.         } else
  1555.         if (strcmp(name, "POSI")==0) {
  1556.             if (!desc->posi) {
  1557.                 if (!(desc->posi=(XYZ_st*)malloc(sizeof(XYZ_st))))
  1558.                     { OUT_MEM("POSI"); }
  1559.             }
  1560.             stuff_XYZ(desc->posi);
  1561.         } else
  1562.         if (strcmp(name, "AXIS")==0) {
  1563.             if (!desc->axis) {
  1564.                 if (!(desc->axis=(AXIS*)malloc(sizeof(AXIS))))
  1565.                     { OUT_MEM("AXIS"); }
  1566.                 bzero((char*)desc->axis, sizeof(AXIS));
  1567.                 desc->axis->xaxi.x = 1;
  1568.                 desc->axis->yaxi.y = 1;
  1569.                 desc->axis->zaxi.z = 1;
  1570.             }
  1571.             parse_word(name, 4, 1);        /* Get field */
  1572.             if        (strcmp(name, "XAXI")==0) stuff_XYZ(&desc->axis->xaxi);
  1573.             else if (strcmp(name, "YAXI")==0) stuff_XYZ(&desc->axis->yaxi);
  1574.             else if (strcmp(name, "ZAXI")==0) stuff_XYZ(&desc->axis->zaxi);
  1575.             else {
  1576.                 if (verbose_flag)
  1577.                     fprintf(stderr, "WARNING: Line %d: Unknown AXIS field: '%s'\n",
  1578.                         world->cur_line, name);
  1579.                 continue;
  1580.             }
  1581.         } else
  1582.         if (strcmp(name, "SIZE")==0) {
  1583.             if (!desc->size) {
  1584.                 if (!(desc->size=(XYZ_st*)malloc(sizeof(XYZ_st))))
  1585.                     { OUT_MEM("SIZE"); }
  1586.             }
  1587.             stuff_XYZ(desc->size);
  1588.         } else
  1589.         if (strcmp(name, "COLR")==0) {
  1590.             if (!desc->colr) {
  1591.                 if (!(desc->colr = (RGB_st*)malloc(sizeof(RGB_st))))
  1592.                     { OUT_MEM("COLR"); }
  1593.             }
  1594.             stuff_RGB((RGB_st*)&defclst[0]);
  1595.             desc->colr->r = defclst[0];
  1596.             desc->colr->g = defclst[1];
  1597.             desc->colr->b = defclst[2];
  1598. #ifdef DO_I_REALLY_WANT_TO_DO_THIS
  1599.             if (desc->fcount) {    /* Already initialized... rewrite */
  1600.                 for (i=3*desc->fcount; i; ) {        /* Reverse - efficiency */
  1601.                     desc->clst[--i] = defclst[2];
  1602.                     desc->clst[--i] = defclst[1];
  1603.                     desc->clst[--i] = defclst[0];
  1604.                 }
  1605.             }
  1606. #endif
  1607.         } else
  1608.         if (strcmp(name, "REFL")==0) {
  1609.             if (!desc->refl) {
  1610.                 if (!(desc->refl = (RGB_st*)malloc(sizeof(RGB_st))))
  1611.                     { OUT_MEM("REFL"); }
  1612.             }
  1613.             stuff_RGB((RGB_st*)&defrlst[0]);
  1614.             desc->refl->r = defrlst[0];
  1615.             desc->refl->g = defrlst[1];
  1616.             desc->refl->b = defrlst[2];
  1617. #ifdef DO_I_REALLY_WANT_TO_DO_THIS
  1618.             if (desc->fcount) {    /* Already initialized... rewrite */
  1619.                 for (i=3*desc->fcount; i; ) {        /* Reverse - efficiency */
  1620.                     desc->rlst[--i] = defrlst[2];
  1621.                     desc->rlst[--i] = defrlst[1];
  1622.                     desc->rlst[--i] = defrlst[0];
  1623.                 }
  1624.             }
  1625. #endif
  1626.         } else
  1627.         if (strcmp(name, "TRAN")==0) {
  1628.             if (!desc->tran) {
  1629.                 if (!(desc->tran = (RGB_st*)malloc(sizeof(RGB_st))))
  1630.                     { OUT_MEM("TRAN"); }
  1631.             }
  1632.             stuff_RGB((RGB_st*)&deftlst[0]);
  1633.             desc->tran->r = deftlst[0];
  1634.             desc->tran->g = deftlst[1];
  1635.             desc->tran->b = deftlst[2];
  1636. #ifdef DO_I_REALLY_WANT_TO_DO_THIS
  1637.             if (desc->fcount) {    /* Already initialized... rewrite */
  1638.                 for (i=3*desc->fcount; i; ) {        /* Reverse - efficiency */
  1639.                     desc->tlst[--i] = deftlst[2];
  1640.                     desc->tlst[--i] = deftlst[1];
  1641.                     desc->tlst[--i] = deftlst[0];
  1642.                 }
  1643.             }
  1644. #endif
  1645.         } else
  1646.         if (strcmp(name, "SPC1")==0) {
  1647.             if (!desc->spc1) {
  1648.                 if (!(desc->spc1 = (RGB_st*)malloc(sizeof(RGB_st))))
  1649.                     { OUT_MEM("SPC1"); }
  1650.             }
  1651.             stuff_RGB((RGB_st*)&defspc1[0]);
  1652.             desc->spc1->r = defspc1[0];
  1653.             desc->spc1->g = defspc1[1];
  1654.             desc->spc1->b = defspc1[2];
  1655.         } else
  1656.         if (strcmp(name, "TPAR")==0) {
  1657.             if (!desc->tpar) {
  1658.                 if (!(desc->tpar=(double*)malloc(16*sizeof(double))))
  1659.                     { OUT_MEM("TPAR"); }
  1660.                 bzero((char*)desc->tpar, 16*sizeof(double));
  1661.             }
  1662.             i = get_UWORD();
  1663.             if (i<0 || i>15) {
  1664.                 fprintf(stderr, "WARNING: Line %d: Bad index: TPAR[%d] should be [0..15]\n", world->cur_line, i);
  1665.                 continue;
  1666.             }
  1667.             desc->tpar[i] = get_FRACT();
  1668.         } else
  1669.         if (strcmp(name, "SURF")==0) {
  1670.             if (!desc->surf) {
  1671.                 if (!(desc->surf=(UBYTE*)malloc(5*sizeof(UBYTE))))
  1672.                     { OUT_MEM("SURF"); }
  1673.                 bzero((char*)desc->surf, 5*sizeof(UBYTE));
  1674.             }
  1675.             i = get_UWORD();
  1676.             if (i<0 || i>4) {
  1677.                 fprintf(stderr, "WARNING: Line %d: Bad index: SURF[%d] should be [0..4]\n", world->cur_line, i);
  1678.                 continue;
  1679.             }
  1680.             desc->surf[i] = get_UBYTE();
  1681.         } else
  1682.         if (strcmp(name, "MTTR")==0) {
  1683.             if (!desc->mttr) {
  1684.                 if (!(desc->mttr=(MTTR*)malloc(sizeof(MTTR))))
  1685.                     { OUT_MEM("MTTR"); }
  1686.                 bzero((char*)desc->mttr, sizeof(MTTR));
  1687.             }
  1688.             parse_word(name, 4, 1);        /* Get field */
  1689.             if        (strcmp(name, "TYPE")==0) {
  1690.                 desc->mttr->type = get_UBYTE();
  1691.                 if (desc->mttr->type>4) {
  1692.                     fprintf(stderr, "WARNING: Line %d: Invalid MTTR refraction Type %d.  Must be [0..4]\n", world->cur_line, desc->mttr->type);
  1693.                     desc->mttr->type = 0;
  1694.                     continue;
  1695.                 }
  1696.             } else if (strcmp(name, "INDE")==0) {    /* Must be between 1.0 and 3.55 inclusive */
  1697.                 desc->mttr->indx = get_FRACT();
  1698.                 if (desc->mttr->indx<1.0 || desc->mttr->indx>3.55) {
  1699.                     fprintf(stderr, "WARNING: Line %d: Invalid MTTR Index of refraction.  Must be (1.0 - 3.55)\n", world->cur_line);
  1700.                     desc->mttr->indx = 1.0;
  1701.                     continue;
  1702.                 }
  1703.             } else {
  1704.                 if (verbose_flag)
  1705.                     fprintf(stderr, "WARNING: Line %d: Unknown MTTR field: '%s'\n",
  1706.                         world->cur_line, name);
  1707.                 continue;
  1708.             }
  1709.         } else
  1710.         if (strcmp(name, "SPEC")==0) {
  1711.             if (!desc->spec) {
  1712.                 if (!(desc->spec=(UBYTE*)malloc(2*sizeof(UBYTE))))
  1713.                     { OUT_MEM("SPEC"); }
  1714.                 bzero((char*)desc->spec, 2*sizeof(UBYTE));
  1715.             }
  1716.             parse_word(name, 4, 1);        /* Get field */
  1717.             if        (strcmp(name, "SPEC")==0) desc->spec[0] = get_UBYTE();
  1718.             else if (strcmp(name, "HARD")==0) desc->spec[1] = get_UBYTE();
  1719.             else {
  1720.                 if (verbose_flag)
  1721.                     fprintf(stderr, "WARNING: Line %d: Unknown SPEC field: '%s'\n",
  1722.                         world->cur_line, name);
  1723.                 continue;
  1724.             }
  1725.         } else
  1726.         if (strcmp(name, "PRP0")==0) {
  1727.             if (!desc->prp0) {
  1728.                 if (!(desc->prp0=(UBYTE*)malloc(6*sizeof(UBYTE))))
  1729.                     { OUT_MEM("PRP0"); }
  1730.                 bzero((char*)desc->prp0, 6*sizeof(UBYTE));
  1731.                 desc->prp0[0] = 255;    /* TS defaults */
  1732.                 desc->prp0[3] = 1;
  1733.             }
  1734.             i = get_UWORD();
  1735.             if (i<0 || i>5) {
  1736.                 fprintf(stderr, "WARNING: Line %d: Bad index: PRP0[%d] should be [0..5]\n", world->cur_line, i);
  1737.                 continue;
  1738.             }
  1739.             desc->prp0[i] = get_UBYTE();
  1740.         } else
  1741.         if (strcmp(name, "PRP1")==0) {
  1742.             if (!desc->prp1) {
  1743.                 if (!(desc->prp1=(UBYTE*)malloc(8*sizeof(UBYTE))))
  1744.                     { OUT_MEM("PRP1"); }
  1745.                 bzero((char*)desc->prp1, 8*sizeof(UBYTE));
  1746.             }
  1747.             i = get_UWORD();
  1748.             if (i<0 || i>7) {
  1749.                 fprintf(stderr, "WARNING: Line %d: Bad index: PRP1[%d] should be [0..7]\n", world->cur_line, i);
  1750.                 continue;
  1751.             }
  1752.             desc->prp1[i] = get_UBYTE();
  1753.         } else
  1754.         if (strcmp(name, "TXT2")==0) {
  1755.             i = get_UWORD();
  1756.             if (i<0 || i>3) {
  1757.                 fprintf(stderr, "WARNING: Line %d: TXT2 index must be [0..3].\n",
  1758.                     world->cur_line);
  1759.                 i=3;
  1760.             }
  1761.             if (!desc->txt2[i])
  1762.                 if (!(desc->txt2[i] = (TXT2*)malloc(sizeof(TXT2))))
  1763.                     OUT_MEM("TXT2");
  1764.             parse_word(name, 4, 1);  /* get field */
  1765.             if (strcmp(name, "FLAG") == 0)
  1766.                 desc->txt2[i]->Flags = get_UWORD();
  1767.             else if (strcmp(name, "TRAN") == 0) 
  1768.                 stuff_XYZ(&desc->txt2[i]->TAxis.tran);
  1769.             else if (strcmp(name, "XAXI") == 0) 
  1770.                 stuff_XYZ(&desc->txt2[i]->TAxis.rota1);
  1771.             else if (strcmp(name, "YAXI") == 0) 
  1772.                 stuff_XYZ(&desc->txt2[i]->TAxis.rota2);
  1773.             else if (strcmp(name, "ZAXI") == 0) 
  1774.                 stuff_XYZ(&desc->txt2[i]->TAxis.rota3);
  1775.             else if (strcmp(name, "SCAL") == 0) 
  1776.                 stuff_XYZ(&desc->txt2[i]->TAxis.scal);
  1777.             else if (strcmp(name, "PARA") == 0) {
  1778.                 j = get_UWORD();
  1779.                 desc->txt2[i]->Params[j] = get_FRACT();
  1780.             } else if (strcmp(name, "PFLA") == 0) {
  1781.                 j = get_UWORD();
  1782.                 desc->txt2[i]->PFlags[j] = get_UWORD();
  1783.             } else if (strcmp(name, "SUBG") == 0) {
  1784.                 parse_word(ps, 0, 0);
  1785.                 strncpy(desc->txt2[i]->SubName, ps, 18);
  1786.                 desc->txt2[i]->SubName[18]='\0';
  1787.             } else if (strcmp(name, "TEXT") == 0)
  1788.                 parse_word(desc->txt2[i]->Name,0,0);
  1789.             else {
  1790. #ifdef WARNING
  1791.                 fprintf(stderr, "WARNING: Line %d: Unknown TXT2 field: '%s'\n",
  1792.                     world->cur_line, name);
  1793. #endif
  1794.                 continue;
  1795.             }
  1796.         } else
  1797.         if (strcmp(name, "INTS")==0) {
  1798.             if (!desc->ints) {
  1799.                 if (!(desc->ints=(double*)malloc(sizeof(double))))
  1800.                     { OUT_MEM("INTS"); }
  1801.             }
  1802.             *desc->ints = get_FRACT();
  1803.         } else
  1804.         if (strcmp(name, "INT1")==0) {
  1805.             if (!desc->int1) desc->int1=(XYZ_st*)malloc(sizeof(XYZ_st));
  1806.             if (!desc->int1) { OUT_MEM("INT1"); }
  1807.             stuff_XYZ(desc->int1);
  1808.         } else
  1809.         if (strcmp(name, "STRY")==0) {
  1810.             parse_word(name, 4, 1);        /* Get field */
  1811.             if        (strcmp(name, "PATH")==0) parse_word(desc->stry->path,18,0);
  1812.             else if (strcmp(name, "TRAN")==0) stuff_XYZ(&desc->stry->tran);
  1813.             else if (strcmp(name, "ROTA")==0) stuff_XYZ(&desc->stry->rota);
  1814.             else if (strcmp(name, "SCAL")==0) stuff_XYZ(&desc->stry->scal);
  1815.             else
  1816.             if (strcmp(name, "INFO")==0) {
  1817.                 desc->stry->info = 0;
  1818.                 while (strin[0]) {
  1819.                     parse_word(ps, 7, 1);
  1820.                     if        (strcmp(ps, "ABS_TRA")==0) desc->stry->info|=(1<<0);
  1821.                     else if (strcmp(ps, "ABS_ROT")==0) desc->stry->info|=(1<<1);
  1822.                     else if (strcmp(ps, "ABS_SCL")==0) desc->stry->info|=(1<<2);
  1823.                     else if (strcmp(ps, "LOC_TRA")==0) desc->stry->info|=(1<<4);
  1824.                     else if (strcmp(ps, "LOC_ROT")==0) desc->stry->info|=(1<<5);
  1825.                     else if (strcmp(ps, "LOC_SCL")==0) desc->stry->info|=(1<<6);
  1826.                     else if (strcmp(ps, "X_ALIGN")==0) desc->stry->info|=(1<<8);
  1827.                     else if (strcmp(ps, "Y_ALIGN")==0) desc->stry->info|=(1<<9);
  1828.                     else if (strcmp(ps, "Z_ALIGN")==0) desc->stry->info|=(1<<10);
  1829.                     else if (strcmp(ps, "FOLLOW_")==0) desc->stry->info|=(1<<12);
  1830.                     else {
  1831.                         if (verbose_flag)
  1832.                             fprintf(stderr, "WARNING: Line %d: Unknown STRY INFO flag: '%s'\n",
  1833.                                 world->cur_line, ps);
  1834.                         continue;
  1835.                     }
  1836.                 }
  1837.             } else {
  1838.                 if (verbose_flag)
  1839.                     fprintf(stderr, "WARNING: Line %d: Unknown STRY field: '%s'\n",
  1840.                         world->cur_line, name);
  1841.                 continue;
  1842.             }
  1843.         } else
  1844.         if (strcmp(name, "END")==0) {
  1845.             parse_word(name, 4, 1);
  1846.             if (strcmp(name, "DESC")!=0)
  1847.                 fprintf(stderr, "WARNING: Line %d: Expected 'END DESC' but got: 'END %s'\n", world->cur_line, name);
  1848.             parse_word(ps, 0, 0);    /* Get the quote, if any */
  1849.             if (strcmp(this_level, ps)!=0)
  1850.                 fprintf(stderr, "WARNING: Line %d: 'DESC Begin' and 'End DESC' quoted comments do not match\n", world->cur_line);
  1851.             break;
  1852.         } else if (verbose_flag)
  1853.             fprintf(stderr, "WARNING: Line %d: Unknown DESC sub-sub-chunk: '%s'\n",
  1854.                 world->cur_line, name);
  1855.     }
  1856. }
  1857.  
  1858. /**************************************************************************/
  1859.  
  1860. /* readTistg.c - read a Textual Imagine Stage file
  1861.  *            - written by Glenn M. Lewis - 8/11/92
  1862.  */
  1863.  
  1864. static void process_OSIZ();
  1865. static void process_POSN();
  1866. static void process_ALGN();
  1867. static void process_PALN();
  1868. static void process_TALN();
  1869. static void process_PTH2();
  1870. static void process_GLB2();
  1871. static void process_AXIS();
  1872. static void process_LITE();
  1873. static void process_FILE();
  1874. static void process_SOBJ();
  1875.  
  1876. static void process_ISTG(world)
  1877. WORLD *world;
  1878. {
  1879.     char name[5];
  1880.     char this_level[MAXLINE];
  1881.  
  1882.     if (world->istg) {
  1883.         fprintf(stderr, "Parse error: More than one ISTG chunk!\n"); exit(-1); }
  1884.     /* Allocate the ISTG structure */
  1885.     if (!(world->istg = (ISTG*)malloc(sizeof(ISTG)))) { OUT_MEM("ISTG"); }
  1886.     bzero((char*)world->istg, sizeof(ISTG));
  1887.  
  1888.     parse_word(this_level, 0, 1);    /* No limit, uppercase. */
  1889.     if (strcmp(this_level, "BEGIN") != 0) {
  1890.         fprintf(stderr, "WARNING: Line %d: Bad syntax.  Missing 'Begin' after 'ISTG'\n",
  1891.             world->cur_line);
  1892.     }
  1893.     parse_word(this_level, 0, 0);    /* No limit, original case */
  1894.  
  1895.     while (get_line(strin, world)) {
  1896.         parse_word(name, 4, 1);        /* Get command */
  1897.         if (strcmp(name, "MAXF")==0) {
  1898.             world->istg->maxf = get_UWORD();
  1899.         } else
  1900.         if (strcmp(name, "SOBJ")==0) {    process_SOBJ(world);
  1901.         } else if (strcmp(name, "END")==0) {
  1902.             parse_word(name, 4, 1);
  1903.             if (strcmp(name, "ISTG")!=0)
  1904.                 fprintf(stderr, "WARNING: Line %d: Expected 'END ISTG' but got: 'END %s'\n", world->cur_line, name);
  1905.             parse_word(ps, 0, 0);    /* Get the quote, if any */
  1906.             if (strcmp(this_level, ps)!=0)
  1907.                 fprintf(stderr, "WARNING: Line %d: 'ISTG Begin' and 'End ISTG' quoted comments do not match\n", world->cur_line);
  1908.             break;
  1909.         } else {
  1910.             if (verbose_flag)
  1911.                 fprintf(stderr, "WARNING: Line %d: Unknown ISTG sub-sub-chunk: '%s'\n",
  1912.                     world->cur_line, name);
  1913.             continue;
  1914.         }
  1915.     }
  1916. /* All done. */
  1917. }
  1918.  
  1919. extern SOBJ *add_SOBJ();    /* to tail of ISTG's SOBJ list */
  1920.  
  1921. static void process_SOBJ(world)
  1922. WORLD *world;
  1923. {
  1924.     char this_level[MAXLINE], name[5];
  1925.     SOBJ *sobj;
  1926.  
  1927.     if (verbose_flag>1) fprintf(stderr, "SOBJ chunk.\n");
  1928.     parse_word(this_level, 0, 1);    /* No limit, uppercase. */
  1929.     if (strcmp(this_level, "BEGIN") != 0) {
  1930.         fprintf(stderr, "WARNING: Line %d: Bad syntax.  Missing 'Begin' after 'SOBJ'\n",
  1931.             world->cur_line);
  1932.     }
  1933.     /* Add another SOBJ to the current ISTG list */
  1934.     parse_word(this_level, 0, 0);
  1935.     sobj = add_SOBJ(world->istg);
  1936.     while (get_line(strin, world)) {
  1937.         parse_word(name, 4, 1);
  1938.         if (verbose_flag>1) fprintf(stderr, "%s chunk.\n", name);
  1939.         if (strcmp(name, "NAME")==0) {
  1940.             parse_word(ps, 0, 0);
  1941.             strncpy(sobj->name, ps, 18);
  1942.             sobj->name[18] = '\0';
  1943.         } else if (strcmp(name, "STGF")==0) { sobj->stgf = get_UWORD();
  1944.         } else if (strcmp(name, "OSIZ")==0) { process_OSIZ(sobj, world);
  1945.         } else if (strcmp(name, "POSN")==0) { process_POSN(sobj, world);
  1946.         } else if (strcmp(name, "ALGN")==0) { process_ALGN(sobj, world);
  1947.         } else if (strcmp(name, "PALN")==0) { process_PALN(sobj, world);
  1948.         } else if (strcmp(name, "TALN")==0) { process_TALN(sobj, world);
  1949.         } else if (strcmp(name, "PTH2")==0) { process_PTH2(sobj, world);
  1950.         } else if (strcmp(name, "GLB2")==0) { process_GLB2(sobj, world);
  1951.         } else if (strcmp(name, "AXIS")==0) { process_AXIS(sobj, world);
  1952.         } else if (strcmp(name, "LITE")==0) { process_LITE(sobj, world);
  1953.         } else if (strcmp(name, "FILE")==0) { process_FILE(sobj, world);
  1954.         } else if (strcmp(name, "END")==0) {
  1955.             parse_word(name, 4, 1);
  1956.             if (strcmp(name, "SOBJ")!=0)
  1957.                 fprintf(stderr, "WARNING: Line %d: Expected 'END SOBJ' but got: 'END %s'\n", world->cur_line, name);
  1958.             parse_word(ps, 0, 0);    /* Get the quote, if any */
  1959.             if (strcmp(this_level, ps)!=0)
  1960.                 fprintf(stderr, "WARNING: Line %d: 'SOBJ Begin' and 'End SOBJ' quoted comments do not match\n", world->cur_line);
  1961.             break;
  1962.         } else {
  1963.             if (verbose_flag)
  1964.                 fprintf(stderr, "WARNING: Line %d: Unknown SOBJ sub-sub-chunk: '%s'\n",
  1965.                     world->cur_line, name);
  1966.             continue;
  1967.         }
  1968.     }
  1969. }
  1970.  
  1971. static void process_OSIZ(sobj, world)
  1972. SOBJ *sobj;
  1973. WORLD *world;
  1974. {
  1975.     char name[5];
  1976.     register OSIZ *osiz;
  1977.  
  1978.     if (!(osiz = (OSIZ*)malloc(sizeof(OSIZ)))) OUT_MEM("OSIZ");
  1979.     bzero((char*)osiz, sizeof(OSIZ));
  1980.     osiz->start = osiz->stop = 1;
  1981.     while (1) {
  1982.         parse_word(name, 4, 1);
  1983.         if (!name[0]) break;
  1984.         if      (strcmp(name, "FLAG")==0) osiz->flags = get_UWORD();
  1985.         else if (strcmp(name, "STAR")==0) osiz->start = get_UWORD();
  1986.         else if (strcmp(name, "STOP")==0) osiz->stop  = get_UWORD();
  1987.         else if (strcmp(name, "SIZE")==0) stuff_XYZ(&osiz->size);
  1988.         else {
  1989.             if (verbose_flag)
  1990.                 fprintf(stderr, "WARNING: Line %d: Unknown OSIZ sub-sub-chunk: '%s'\n",
  1991.                     world->cur_line, name);
  1992.         }
  1993.     }
  1994.     insert_into_sorted_list((PALN**)&sobj->osiz, (PALN*)osiz);
  1995. }
  1996.  
  1997. static void process_POSN(sobj, world)
  1998. SOBJ *sobj;
  1999. WORLD *world;
  2000. {
  2001.     char name[5];
  2002.     register POSN *posn;
  2003.  
  2004.     if (!(posn = (POSN*)malloc(sizeof(POSN)))) OUT_MEM("POSN");
  2005.     bzero((char*)posn, sizeof(POSN));
  2006.     posn->start = posn->stop = 1;
  2007.     while (1) {
  2008.         parse_word(name, 4, 1);
  2009.         if (!name[0]) break;
  2010.         if      (strcmp(name, "FLAG")==0) posn->flags = get_UWORD();
  2011.         else if (strcmp(name, "STAR")==0) posn->start = get_UWORD();
  2012.         else if (strcmp(name, "STOP")==0) posn->stop  = get_UWORD();
  2013.         else if (strcmp(name, "POSN")==0) stuff_XYZ(&posn->posn);
  2014.         else {
  2015.             if (verbose_flag)
  2016.                 fprintf(stderr, "WARNING: Line %d: Unknown POSN sub-sub-chunk: '%s'\n",
  2017.                     world->cur_line, name);
  2018.         }
  2019.     }
  2020.     insert_into_sorted_list((PALN**)&sobj->posn, (PALN*)posn);
  2021. }
  2022.  
  2023. static void process_ALGN(sobj, world)
  2024. SOBJ *sobj;
  2025. WORLD *world;
  2026. {
  2027.     char name[5];
  2028.     register ALGN *algn;
  2029.  
  2030.     if (!(algn = (ALGN*)malloc(sizeof(ALGN)))) OUT_MEM("ALGN");
  2031.     bzero((char*)algn, sizeof(ALGN));
  2032.     algn->start = algn->stop = 1;
  2033.     while (1) {
  2034.         parse_word(name, 4, 1);
  2035.         if (!name[0]) break;
  2036.         if      (strcmp(name, "FLAG")==0) algn->flags = get_UWORD();
  2037.         else if (strcmp(name, "STAR")==0) algn->start = get_UWORD();
  2038.         else if (strcmp(name, "STOP")==0) algn->stop  = get_UWORD();
  2039.         else if (strcmp(name, "ALGN")==0) stuff_XYZ(&algn->algn);
  2040.         else {
  2041.             if (verbose_flag)
  2042.                 fprintf(stderr, "WARNING: Line %d: Unknown ALGN sub-sub-chunk: '%s'\n",
  2043.                     world->cur_line, name);
  2044.         }
  2045.     }
  2046.     insert_into_sorted_list((PALN**)&sobj->algn, (PALN*)algn);
  2047. }
  2048.  
  2049. static void process_PALN(sobj, world)
  2050. SOBJ *sobj;
  2051. WORLD *world;
  2052. {
  2053.     char name[5];
  2054.     register PALN *paln;
  2055.  
  2056.     if (!(paln = (PALN*)malloc(sizeof(PALN)))) OUT_MEM("PALN");
  2057.     bzero((char*)paln, sizeof(PALN));
  2058.     paln->start = paln->stop = 1;
  2059.     while (1) {
  2060.         parse_word(name, 4, 1);
  2061.         if (!name[0]) break;
  2062.         if      (strcmp(name, "FLAG")==0) paln->flags = get_UWORD();
  2063.         else if (strcmp(name, "STAR")==0) paln->start = get_UWORD();
  2064.         else if (strcmp(name, "STOP")==0) paln->stop  = get_UWORD();
  2065.         else {
  2066.             if (verbose_flag)
  2067.                 fprintf(stderr, "WARNING: Line %d: Unknown PALN sub-sub-chunk: '%s'\n",
  2068.                     world->cur_line, name);
  2069.         }
  2070.     }
  2071.     insert_into_sorted_list(&sobj->paln, paln);
  2072. }
  2073.  
  2074. static void process_TALN(sobj, world)
  2075. SOBJ *sobj;
  2076. WORLD *world;
  2077. {
  2078.     char name[5];
  2079.     register TALN *taln;
  2080.  
  2081.     if (!(taln = (TALN*)malloc(sizeof(TALN)))) OUT_MEM("TALN");
  2082.     bzero((char*)taln, sizeof(TALN));
  2083.     taln->start = taln->stop = 1;
  2084.     while (1) {
  2085.         parse_word(name, 4, 1);
  2086.         if (!name[0]) break;
  2087.         if      (strcmp(name, "FLAG")==0) taln->flags = get_UWORD();
  2088.         else if (strcmp(name, "STAR")==0) taln->start = get_UWORD();
  2089.         else if (strcmp(name, "STOP")==0) taln->stop  = get_UWORD();
  2090.         else if (strcmp(name, "INIT")==0) taln->initial_y = get_FRACT();
  2091.         else if (strcmp(name, "FINA")==0) taln->final_y   = get_FRACT();
  2092.         else if (strcmp(name, "TRAC")==0 || strcmp(name, "NAME")==0 ||
  2093.             strcmp(name, "OBJE")==0) {
  2094.             parse_word(taln->trackobj, 0, 0);
  2095.         } else {
  2096.             if (verbose_flag)
  2097.                 fprintf(stderr, "WARNING: Line %d: Unknown TALN sub-sub-chunk: '%s'\n",
  2098.                     world->cur_line, name);
  2099.         }
  2100.     }
  2101.     insert_into_sorted_list((PALN**)&sobj->taln, (PALN*)taln);
  2102. }
  2103.  
  2104. static void process_PTH2(sobj, world)
  2105. SOBJ *sobj;
  2106. WORLD *world;
  2107. {
  2108.     char name[128];
  2109.     register PTH2 *pth2;
  2110.  
  2111.     if (!(pth2 = (PTH2*)malloc(sizeof(PTH2)))) OUT_MEM("PTH2");
  2112.     bzero((char*)pth2, sizeof(PTH2));
  2113.     pth2->start = pth2->stop = 1;
  2114.     while (1) {
  2115.         parse_word(name, 0, 1);        /* Don't limit the word size */
  2116.         if (!name[0]) break;
  2117.         if      (strncmp(name, "FLAG",4)==0) pth2->flags = get_UWORD();
  2118.         else if (strcmp(name, "START")==0) pth2->start = get_UWORD();
  2119.         else if (strncmp(name, "STOP",4)==0) pth2->stop  = get_UWORD();
  2120.         else if (strncmp(name, "ACCE",4)==0) pth2->acceleration_frames = get_ULONG();
  2121.         else if (strncmp(name, "START_",6)==0) pth2->start_speed=get_FRACT();
  2122.         else if (strncmp(name, "DECEL",5)==0) pth2->deacceleration_frames = get_ULONG();
  2123.         else if (strncmp(name, "END_",4)==0) pth2->end_speed = get_FRACT();
  2124.         else if (strcmp(name, "PATH")==0 || strcmp(name, "NAME")==0) {
  2125.             parse_word(pth2->path, 0, 0);
  2126.         } else {
  2127.             if (verbose_flag)
  2128.                 fprintf(stderr, "WARNING: Line %d: Unknown PTH2 sub-sub-chunk: '%s'\n",
  2129.                     world->cur_line, name);
  2130.         }
  2131.     }
  2132.     insert_into_sorted_list((PALN**)&sobj->pth2, (PALN*)pth2);
  2133. }
  2134.  
  2135. static void process_GLB2(sobj, world)
  2136. SOBJ *sobj;
  2137. WORLD *world;
  2138. {
  2139.     char name[128];
  2140.     register GLB2 *glb2;
  2141.  
  2142.     if (!(glb2 = (GLB2*)malloc(sizeof(GLB2)))) OUT_MEM("GLB2");
  2143.     bzero((char*)glb2, sizeof(GLB2));
  2144.     glb2->start = glb2->stop = 1;
  2145.     while (1) {
  2146.         parse_word(name, 0, 1);        /* Don't limit the word size */
  2147.         if (!name[0]) break;
  2148.         if      (strncmp(name, "FLAG",4)==0) glb2->flags = get_UWORD();
  2149.         else if (strcmp(name, "START")==0) glb2->start = get_UWORD();
  2150.         else if (strcmp(name, "STOP")==0) glb2->stop  = get_UWORD();
  2151.         else if (strncmp(name, "SKY_",4)==0) glb2->sky_blending = get_ULONG();
  2152.         else if (strncmp(name, "STARF",5)==0) glb2->starfield = get_FRACT();
  2153.         else if (strncmp(name, "TRAN",4)==0) glb2->transition = get_ULONG();
  2154.         /* The following are FRACTional colors */
  2155.         else if (strncmp(name, "AMBI",4)==0) stage_RGB(&glb2->ambient);
  2156.         else if (strncmp(name, "HORI",4)==0) stage_RGB(&glb2->horizon);
  2157.         else if (strcmp(name, "ZENITH1")==0) stage_RGB(&glb2->zenith1);
  2158.         else if (strcmp(name, "ZENITH2")==0) stage_RGB(&glb2->zenith2);
  2159.         else if (strncmp(name, "FOG_C",5)==0) stage_RGB(&glb2->fog_color);
  2160.         else if (strncmp(name, "FOG_B",5)==0) glb2->fog_bottom = get_FRACT();
  2161.         else if (strncmp(name, "FOG_T",5)==0) glb2->fog_top    = get_FRACT();
  2162.         else if (strncmp(name, "FOG_L",5)==0) glb2->fog_length = get_FRACT();
  2163.         else if (strncmp(name, "BRUSH_S",7)==0) glb2->brush_seq  = get_ULONG();
  2164.         else if (strncmp(name, "BACKDROP_S",10)==0) glb2->backdrop_seq = get_ULONG();
  2165.         else if (strcmp(name, "BACKDROP")==0) {
  2166.             parse_word(glb2->backdrop, 0, 0);
  2167.         } else if (strncmp(name, "GLOB",4)==0) {
  2168.             parse_word(glb2->globalbrush, 0, 0);
  2169.         } else {
  2170.             if (verbose_flag)
  2171.                 fprintf(stderr, "WARNING: Line %d: Unknown PTH2 sub-sub-chunk: '%s'\n",
  2172.                     world->cur_line, name);
  2173.         }
  2174.     }
  2175.     insert_into_sorted_list((PALN**)&sobj->glb2, (PALN*)glb2);
  2176. }
  2177.  
  2178. static void process_AXIS(sobj, world)
  2179. SOBJ *sobj;
  2180. WORLD *world;
  2181. {
  2182.     char name[5];
  2183.     register SAXIS *axis;
  2184.  
  2185.     if (!(axis = (SAXIS*)malloc(sizeof(SAXIS)))) OUT_MEM("SAXIS");
  2186.     bzero((char*)axis, sizeof(SAXIS));
  2187.     axis->start = axis->stop = 1;
  2188.     while (1) {
  2189.         parse_word(name, 4, 1);
  2190.         if (!name[0]) break;
  2191.         if      (strcmp(name, "FLAG")==0) axis->flags = get_UWORD();
  2192.         else if (strcmp(name, "STAR")==0) axis->start = get_UWORD();
  2193.         else if (strcmp(name, "STOP")==0) axis->stop  = get_UWORD();
  2194.         else {
  2195.             if (verbose_flag)
  2196.                 fprintf(stderr, "WARNING: Line %d: Unknown AXIS sub-sub-chunk: '%s'\n",
  2197.                     world->cur_line, name);
  2198.         }
  2199.     }
  2200.     insert_into_sorted_list((PALN**)&sobj->axis, (PALN*)axis);
  2201. }
  2202.  
  2203. static void process_LITE(sobj, world)
  2204. SOBJ *sobj;
  2205. WORLD *world;
  2206. {
  2207.     char name[5];
  2208.     register LITE *lite;
  2209.  
  2210.     if (!(lite = (LITE*)malloc(sizeof(LITE)))) OUT_MEM("LITE");
  2211.     bzero((char*)lite, sizeof(LITE));
  2212.     lite->start = lite->stop = 1;
  2213.     while (1) {
  2214.         parse_word(name, 4, 1);
  2215.         if (!name[0]) break;
  2216.         if      (strcmp(name, "FLAG")==0) lite->flags = get_UWORD();
  2217.         else if (strcmp(name, "STAR")==0) lite->start = get_UWORD();
  2218.         else if (strcmp(name, "STOP")==0) lite->stop  = get_UWORD();
  2219.         /* The following is a FRACTional color */
  2220.         else if (strcmp(name, "COLO")==0) stage_RGB(&lite->color);
  2221.         else if (strcmp(name, "TRAN")==0) lite->transition = get_ULONG();
  2222.         else {
  2223.             if (verbose_flag)
  2224.                 fprintf(stderr, "WARNING: Line %d: Unknown LITE sub-sub-chunk: '%s'\n",
  2225.                     world->cur_line, name);
  2226.         }
  2227.     }
  2228.     insert_into_sorted_list((PALN**)&sobj->lite, (PALN*)lite);
  2229. }
  2230.  
  2231. static void process_FILE(sobj, world)
  2232. SOBJ *sobj;
  2233. WORLD *world;
  2234. {
  2235.     char name[5];
  2236.     register SFILE *file;
  2237.  
  2238.     if (!(file = (SFILE*)malloc(sizeof(SFILE)))) OUT_MEM("SFILE");
  2239.     bzero((char*)file, sizeof(SFILE));
  2240.     file->start = file->stop = 1;
  2241.     while (1) {
  2242.         parse_word(name, 4, 1);
  2243.         if (!name[0]) break;
  2244.         if      (strcmp(name, "FLAG")==0) file->flags = get_UWORD();
  2245.         else if (strcmp(name, "STAR")==0) file->start = get_UWORD();
  2246.         else if (strcmp(name, "STOP")==0) file->stop  = get_UWORD();
  2247.         else if (strcmp(name, "CYCL")==0) file->cycles_to_perform = get_FRACT();
  2248.         else if (strcmp(name, "INIT")==0) file->initial_cycle_phase = get_FRACT();
  2249.         else if (strcmp(name, "TRAN")==0) file->transition = get_ULONG();
  2250.         else if (strcmp(name, "DESC")==0 || strcmp(name, "NAME")==0) {
  2251.             parse_word(file->object_description, 0, 0);
  2252.         } else {
  2253.             if (verbose_flag)
  2254.                 fprintf(stderr, "WARNING: Line %d: Unknown FILE sub-sub-chunk: '%s'\n",
  2255.                     world->cur_line, name);
  2256.         }
  2257.     }
  2258.     insert_into_sorted_list((PALN**)&sobj->file, (PALN*)file);
  2259. }
  2260.  
  2261.